Skip to content

Commit da021c7

Browse files
committed
feat: add existing config edit
1 parent f6139d7 commit da021c7

4 files changed

Lines changed: 168 additions & 17 deletions

File tree

src/builder_page.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class BuilderPage(Gtk.Box):
3636
builder_toast_overlay: Adw.ToastOverlay = Gtk.Template.Child()
3737
form_name_row: Adw.EntryRow = Gtk.Template.Child()
3838
fields_group: Adw.PreferencesGroup = Gtk.Template.Child()
39+
back_button: Gtk.Button = Gtk.Template.Child()
3940

4041
def __init__(self, **kwargs):
4142
super().__init__(**kwargs)
@@ -59,6 +60,13 @@ def load_from_model(self, model: FormModel):
5960
for ff in model.fields:
6061
self._append_row(ff)
6162

63+
def set_save_path(self, path: str):
64+
"""
65+
Pre-seed the save path so the file dialog opens at the original
66+
file's location and the user can overwrite with one click.
67+
"""
68+
self._save_path = path
69+
6270
@Gtk.Template.Callback()
6371
def on_add_field_clicked(self, *_):
6472
dialog = FieldTypeDialog(self._add_field_of_type)
@@ -68,8 +76,6 @@ def on_add_field_clicked(self, *_):
6876
def on_save_clicked(self, *_):
6977
file_dialog = Gtk.FileDialog()
7078
file_dialog.set_title("Save Form Config")
71-
safe_name = (self.model.form_name or "form").lower().replace(" ", "_")
72-
file_dialog.set_initial_name(safe_name + ".json")
7379

7480
json_filter = Gtk.FileFilter()
7581
json_filter.set_name("JSON files")
@@ -79,6 +85,15 @@ def on_save_clicked(self, *_):
7985
filters.append(json_filter)
8086
file_dialog.set_filters(filters)
8187

88+
if self._save_path:
89+
# Editing an existing file: open the dialog in the same folder
90+
# with the same filename pre-filled so one click overwrites it.
91+
existing = Gio.File.new_for_path(self._save_path)
92+
file_dialog.set_initial_file(existing)
93+
else:
94+
safe_name = (self.model.form_name or "form").lower().replace(" ", "_")
95+
file_dialog.set_initial_name(safe_name + ".json")
96+
8297
file_dialog.save(self.get_root(), None, self._on_save_finish)
8398

8499
def _on_form_name_changed(self, row):
@@ -158,6 +173,32 @@ def _on_save_finish(self, dialog, result):
158173
if hasattr(self, "page") and self.page and hasattr(self.page, "tab_page"):
159174
self.page.tab_page.set_title(os.path.basename(path))
160175

176+
# Unlock Back now that there is a saved file to return with
177+
self.back_button.set_sensitive(True)
178+
179+
@Gtk.Template.Callback()
180+
def on_back_clicked(self, *_):
181+
"""
182+
Return to FormConfig with the saved JSON pre-selected.
183+
The user just needs to pick a CSV and hit Open Form.
184+
Only reachable after at least one successful Save.
185+
"""
186+
if not self._save_path:
187+
return
188+
189+
from .form_config import FormConfig
190+
191+
self.page.remove(self)
192+
193+
form_config = FormConfig()
194+
form_config.set_page(self.page)
195+
self.page.append(form_config)
196+
197+
saved_file = Gio.File.new_for_path(self._save_path)
198+
form_config.preselect_config(saved_file)
199+
200+
self.page.tab_page.set_title(os.path.basename(self._save_path))
201+
161202
def _show_toast(self, message: str, timeout: int = 3):
162203
toast = Adw.Toast.new(message)
163204
toast.set_timeout(timeout)

src/builder_page.ui

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
</property>
5858
</object>
5959
</child>
60-
6160
</object>
6261
</child>
6362
</object>
@@ -77,11 +76,44 @@
7776
</object>
7877
</child>
7978
<child>
80-
<object class="GtkButton">
81-
<property name="label" translatable="yes">Save Form</property>
82-
<property name="halign">center</property>
83-
<property name="css-classes">pill</property>
84-
<signal name="clicked" handler="on_save_clicked"/>
79+
<object class="AdwClamp">
80+
<property name="maximum-size">600</property>
81+
<property name="tightening-threshold">400</property>
82+
<child>
83+
<object class="GtkBox">
84+
<property name="orientation">horizontal</property>
85+
<property name="spacing">8</property>
86+
<property name="homogeneous">True</property>
87+
<child>
88+
<!-- Back: navigates to FormConfig, pre-seeding the saved JSON -->
89+
<object class="GtkButton" id="back_button">
90+
<property name="halign">fill</property>
91+
<property name="sensitive">0</property>
92+
<property name="tooltip-text" translatable="yes">Go back to the forms page with this config selected</property>
93+
<signal name="clicked" handler="on_back_clicked"/>
94+
<property name="child">
95+
<object class="AdwButtonContent">
96+
<property name="icon-name">go-previous-symbolic</property>
97+
<property name="label" translatable="yes">Back</property>
98+
</object>
99+
</property>
100+
</object>
101+
</child>
102+
<child>
103+
<!-- Save: writes JSON, enables Back -->
104+
<object class="GtkButton">
105+
<property name="halign">fill</property>
106+
<signal name="clicked" handler="on_save_clicked"/>
107+
<property name="child">
108+
<object class="AdwButtonContent">
109+
<property name="icon-name">document-save-symbolic</property>
110+
<property name="label" translatable="yes">Save</property>
111+
</object>
112+
</property>
113+
</object>
114+
</child>
115+
</object>
116+
</child>
85117
</object>
86118
</child>
87119
</object>
@@ -92,3 +124,4 @@
92124
</child>
93125
</template>
94126
</interface>
127+

src/form_config.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ class FormConfig(Gtk.Box):
3838
open_csv_btn: Gtk.Button = Gtk.Template.Child()
3939
create_form_btn: Gtk.Button = Gtk.Template.Child()
4040
new_csv_btn: Gtk.Button = Gtk.Template.Child()
41-
build_form_btn: Gtk.Button = Gtk.Template.Child() # NEW
41+
build_form_btn: Gtk.Button = Gtk.Template.Child()
42+
edit_in_builder_btn: Gtk.Button = Gtk.Template.Child()
4243

4344
def __init__(self, **kwargs):
4445
super().__init__(**kwargs)
@@ -52,6 +53,7 @@ def __init__(self, **kwargs):
5253
self.open_csv_btn.connect("clicked", self._open_csv)
5354
self.new_csv_btn.connect("clicked", self._create_new_csv)
5455
self.build_form_btn.connect("clicked", self._open_builder)
56+
self.edit_in_builder_btn.connect("clicked", self._edit_in_builder)
5557

5658
def set_page(self, page):
5759
"""
@@ -108,6 +110,7 @@ def _on_json_selected(self, dialog, result):
108110
self.page.config_file = file
109111
self.page.form_config = self._load_config(file)
110112
self.open_form_config_btn.set_label(file.get_basename())
113+
self.edit_in_builder_btn.set_sensitive(True)
111114
self._try_form_open()
112115
except GLib.Error:
113116
pass # cancelled
@@ -150,11 +153,61 @@ def _open_form_window(self, _button, __):
150153
form_page.set_page(self.page)
151154
self.page.append(form_page)
152155

156+
def preselect_config(self, file: Gio.File):
157+
"""
158+
Called by BuilderPage after saving to pre-fill the JSON config
159+
field, so the user only needs to pick a CSV and click Open Form.
160+
Mirrors what _on_json_selected does but without the file dialog.
161+
"""
162+
self.page.config_file = file
163+
self.page.form_config = self._load_config(file)
164+
self.open_form_config_btn.set_label(file.get_basename())
165+
self.edit_in_builder_btn.set_sensitive(True)
166+
self._try_form_open()
167+
153168
def _open_builder(self, *_):
154-
self.page.tab_page.set_title("✏ New Form")
169+
self.page.tab_page.set_title("New Form")
170+
171+
self.page.remove(self)
172+
173+
builder = BuilderPage()
174+
builder.set_page(self.page)
175+
self.page.append(builder)
176+
177+
def _edit_in_builder(self, *_):
178+
"""
179+
Open the already-selected JSON config in the builder for editing.
180+
Parses it into a FormModel, populates the BuilderPage, then swaps
181+
in the builder exactly like _open_builder does.
182+
On save the user can overwrite the original file or choose a new path.
183+
"""
184+
from .form_model import FormModel
185+
186+
config_file = self.page.config_file
187+
if not config_file:
188+
return
189+
190+
path = config_file.get_path()
191+
if not path:
192+
return
193+
194+
try:
195+
model = FormModel.from_file(path)
196+
except Exception as e:
197+
# Malformed JSON — surface the error via the existing toast pattern
198+
# by falling back gracefully; the file was already validated by
199+
# _load_config so this should only happen on disk race conditions.
200+
print(f"Could not parse config for editing: {e}")
201+
return
202+
203+
self.page.tab_page.set_title(f"✏ {model.form_name or config_file.get_basename()}")
155204

156205
self.page.remove(self)
157206

158207
builder = BuilderPage()
159208
builder.set_page(self.page)
209+
builder.load_from_model(model)
210+
# Pre-set the save dialog's suggested path to the original file
211+
# so the user can hit Save and overwrite in one click.
212+
builder.set_save_path(path)
160213
self.page.append(builder)

src/form_config.ui

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,37 @@
3030
</object>
3131
</child>
3232
<child>
33-
<object class="GtkButton" id="open_form_config_btn">
34-
<property name="child">
35-
<object class="AdwButtonContent">
36-
<property name="icon-name">document-open-symbolic</property>
37-
<property name="label" translatable="yes">_Open Existing</property>
38-
<property name="use-underline">True</property>
33+
<object class="GtkBox">
34+
<property name="orientation">0</property>
35+
<property name="spacing">8</property>
36+
<property name="halign">center</property>
37+
<property name="margin-top">10</property>
38+
<child>
39+
<!-- Open JSON -->
40+
<object class="GtkButton" id="open_form_config_btn">
41+
<property name="child">
42+
<object class="AdwButtonContent">
43+
<property name="icon-name">document-open-symbolic</property>
44+
<property name="label" translatable="yes">_Open</property>
45+
<property name="use-underline">True</property>
46+
</object>
47+
</property>
3948
</object>
40-
</property>
49+
</child>
50+
<child>
51+
<!-- Edit in Builder — sensitive only after a config is selected -->
52+
<object class="GtkButton" id="edit_in_builder_btn">
53+
<property name="sensitive">0</property>
54+
<property name="tooltip-text" translatable="yes">Edit this config in the visual builder</property>
55+
<property name="child">
56+
<object class="AdwButtonContent">
57+
<property name="icon-name">document-edit-symbolic</property>
58+
<property name="label" translatable="yes">_Edit</property>
59+
<property name="use-underline">True</property>
60+
</object>
61+
</property>
62+
</object>
63+
</child>
4164
</object>
4265
</child>
4366
</object>
@@ -92,3 +115,4 @@
92115
</child>
93116
</template>
94117
</interface>
118+

0 commit comments

Comments
 (0)