Add Page Editor toolbar toggles and widen the default window
Edit mode, snap to grid and the widgets/properties pane are now toggles on the main toolbar while the Page Editor is open. Outside edit mode the page and the property sheet are read-only; drags snap to the 10px grid when snapping is on. The default window grows by the right pane's width so the canvas is not squeezed when the pane shows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
VERSION = "0.0.0.130"
|
VERSION = "0.0.0.131"
|
||||||
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
||||||
DEFAULT_GRID_SIZE = 10
|
DEFAULT_GRID_SIZE = 10
|
||||||
DEFAULT_ADD_TIMESTAMP = True
|
DEFAULT_ADD_TIMESTAMP = True
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ PALETTE_REQUESTED_ROWS = 5
|
|||||||
# Below this the shortfall is deep enough to shrink the tree past a usable few rows, and
|
# Below this the shortfall is deep enough to shrink the tree past a usable few rows, and
|
||||||
# ttk has no way to hold a pane at a floor once the room is simply not there.
|
# ttk has no way to hold a pane at a floor once the room is simply not there.
|
||||||
MIN_WINDOW_HEIGHT = 300
|
MIN_WINDOW_HEIGHT = 300
|
||||||
|
# The editor itself, plus room for the Page Editor's widgets/properties pane.
|
||||||
|
DEFAULT_WINDOW_WIDTH = 880 + ox_page_view.RIGHT_PANE_WIDTH
|
||||||
|
DEFAULT_WINDOW_HEIGHT = 600
|
||||||
|
|
||||||
# How many visited views Back and Forward can walk through. A session touches few of
|
# How many visited views Back and Forward can walk through. A session touches few of
|
||||||
# them; the cap only stops an unbounded list.
|
# them; the cap only stops an unbounded list.
|
||||||
@@ -368,11 +371,34 @@ def show_ox_page_view(comp):
|
|||||||
on_close=dismiss_ox_page_view,
|
on_close=dismiss_ox_page_view,
|
||||||
)
|
)
|
||||||
ox_page_panel.frame.pack(fill=tk.BOTH, expand=True)
|
ox_page_panel.frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
_show_page_editor_tools(ox_page_panel)
|
||||||
record_visit(VIEW_PAGE, comp)
|
record_visit(VIEW_PAGE, comp)
|
||||||
update_zoom_label()
|
update_zoom_label()
|
||||||
app_state.set_status(f"Page Editor: {comp.get('name', '?')}")
|
app_state.set_status(f"Page Editor: {comp.get('name', '?')}")
|
||||||
|
|
||||||
|
|
||||||
|
def _show_page_editor_tools(panel):
|
||||||
|
"""Lays the Page Editor's toggles out on the toolbar, after a separator of their own."""
|
||||||
|
_hide_page_editor_tools()
|
||||||
|
ttk.Separator(page_editor_tools, orient=tk.VERTICAL).pack(
|
||||||
|
side=tk.LEFT, fill=tk.Y, padx=8, pady=2)
|
||||||
|
for index, (icon, tip, variable, command) in enumerate(panel.toolbar_tools()):
|
||||||
|
if variable is not None:
|
||||||
|
button = ttk.Checkbutton(page_editor_tools, text=icon, width=3,
|
||||||
|
style="Toolbutton", variable=variable, command=command)
|
||||||
|
else:
|
||||||
|
button = ttk.Button(page_editor_tools, text=icon, width=3, command=command)
|
||||||
|
button.pack(side=tk.LEFT, padx=(3 if index else 0, 0))
|
||||||
|
_ToolTip(button, tip)
|
||||||
|
page_editor_tools.pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
|
||||||
|
def _hide_page_editor_tools():
|
||||||
|
for child in page_editor_tools.winfo_children():
|
||||||
|
child.destroy()
|
||||||
|
page_editor_tools.pack_forget()
|
||||||
|
|
||||||
|
|
||||||
def dismiss_ox_page_view(restore_canvas=True):
|
def dismiss_ox_page_view(restore_canvas=True):
|
||||||
"""Takes the OxPage editor down and gives the pane back."""
|
"""Takes the OxPage editor down and gives the pane back."""
|
||||||
global ox_page_panel
|
global ox_page_panel
|
||||||
@@ -381,6 +407,7 @@ def dismiss_ox_page_view(restore_canvas=True):
|
|||||||
|
|
||||||
ox_page_panel.destroy()
|
ox_page_panel.destroy()
|
||||||
ox_page_panel = None
|
ox_page_panel = None
|
||||||
|
_hide_page_editor_tools()
|
||||||
update_zoom_label()
|
update_zoom_label()
|
||||||
|
|
||||||
if restore_canvas:
|
if restore_canvas:
|
||||||
@@ -2386,7 +2413,7 @@ def _live_tree_expand(comp_id, tree_item_id):
|
|||||||
window = tk.Tk()
|
window = tk.Tk()
|
||||||
app_state.window = window
|
app_state.window = window
|
||||||
window.title(app_state.base_window_title())
|
window.title(app_state.base_window_title())
|
||||||
window.geometry("950x600")
|
window.geometry(f"{DEFAULT_WINDOW_WIDTH}x{DEFAULT_WINDOW_HEIGHT}")
|
||||||
# Width is left alone; only the height needs a floor, to keep the tree pane off zero.
|
# Width is left alone; only the height needs a floor, to keep the tree pane off zero.
|
||||||
window.minsize(1, MIN_WINDOW_HEIGHT)
|
window.minsize(1, MIN_WINDOW_HEIGHT)
|
||||||
|
|
||||||
@@ -2588,6 +2615,9 @@ _btn_print = ttk.Button(toolbar, text="🖨", width=3, command=wiresheet.print_s
|
|||||||
_btn_print.pack(side=tk.LEFT)
|
_btn_print.pack(side=tk.LEFT)
|
||||||
_ToolTip(_btn_print, "Print Wiresheet (Ctrl+P)")
|
_ToolTip(_btn_print, "Print Wiresheet (Ctrl+P)")
|
||||||
|
|
||||||
|
# Filled with the Page Editor's own tools while it is open, and empty (unpacked) otherwise.
|
||||||
|
page_editor_tools = ttk.Frame(toolbar)
|
||||||
|
|
||||||
paned_window = ttk.PanedWindow(window, orient=tk.HORIZONTAL)
|
paned_window = ttk.PanedWindow(window, orient=tk.HORIZONTAL)
|
||||||
paned_window.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
paned_window.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
|||||||
+80
-30
@@ -53,6 +53,8 @@ DEFAULT_ZOOM = 0.5
|
|||||||
ZOOM_LEVELS = (0.25, 0.33, 0.5, 0.67, 0.8, 1.0, 1.25, 1.5, 2.0, 3.0)
|
ZOOM_LEVELS = (0.25, 0.33, 0.5, 0.67, 0.8, 1.0, 1.25, 1.5, 2.0, 3.0)
|
||||||
|
|
||||||
CANVAS_PADDING = 16
|
CANVAS_PADDING = 16
|
||||||
|
# About what the widget tree / properties pane asks for (300px sheet + scrollbar).
|
||||||
|
RIGHT_PANE_WIDTH = 320
|
||||||
|
|
||||||
WIDGET_OUTLINE_WIDTH = 1
|
WIDGET_OUTLINE_WIDTH = 1
|
||||||
SELECTED_OUTLINE = "#1f4e79"
|
SELECTED_OUTLINE = "#1f4e79"
|
||||||
@@ -146,8 +148,12 @@ GRID_STEP = 10 # device pixels
|
|||||||
GRID_MIN_SPACING = 4 # canvas pixels; a denser grid is left out rather than drawn solid
|
GRID_MIN_SPACING = 4 # canvas pixels; a denser grid is left out rather than drawn solid
|
||||||
GRID_SHADE = 0.82 # grid line = page colour scaled by this (lifted on dark pages)
|
GRID_SHADE = 0.82 # grid line = page colour scaled by this (lifted on dark pages)
|
||||||
|
|
||||||
# Whether the 10x10 grid is drawn, held for the session like the Sedona sheet's toggles.
|
# Page Editor toggles, held for the session like the Sedona sheet's: whether the page
|
||||||
show_grid = True
|
# can be changed (edit mode, which is also when the 10x10 grid is drawn), whether a
|
||||||
|
# drag lands on the grid, and whether the tree/properties pane shows.
|
||||||
|
edit_mode = False
|
||||||
|
snap_to_grid = True
|
||||||
|
show_right_pane = True
|
||||||
|
|
||||||
|
|
||||||
def _grid_colour(page_hex):
|
def _grid_colour(page_hex):
|
||||||
@@ -200,6 +206,7 @@ class OxPageViewPanel:
|
|||||||
self._build_header()
|
self._build_header()
|
||||||
self._build_body()
|
self._build_body()
|
||||||
self._build_footer()
|
self._build_footer()
|
||||||
|
self._apply_right_pane()
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -233,10 +240,9 @@ class OxPageViewPanel:
|
|||||||
tk.Label(header, textvariable=self._widget_count_var, font=plain_font,
|
tk.Label(header, textvariable=self._widget_count_var, font=plain_font,
|
||||||
fg="#555555", bg=HEADER_BG).pack(side=tk.RIGHT)
|
fg="#555555", bg=HEADER_BG).pack(side=tk.RIGHT)
|
||||||
|
|
||||||
self._grid_var = tk.BooleanVar(value=show_grid)
|
self._edit_var = tk.BooleanVar(value=edit_mode)
|
||||||
tk.Checkbutton(header, text="Grid", variable=self._grid_var, font=plain_font,
|
self._snap_var = tk.BooleanVar(value=snap_to_grid)
|
||||||
bg=HEADER_BG, activebackground=HEADER_BG,
|
self._right_pane_var = tk.BooleanVar(value=show_right_pane)
|
||||||
command=self._on_grid_toggled).pack(side=tk.RIGHT, padx=(0, 12))
|
|
||||||
|
|
||||||
def _build_body(self):
|
def _build_body(self):
|
||||||
body = ttk.PanedWindow(self.frame, orient=tk.HORIZONTAL)
|
body = ttk.PanedWindow(self.frame, orient=tk.HORIZONTAL)
|
||||||
@@ -249,12 +255,15 @@ class OxPageViewPanel:
|
|||||||
# Workbench's layout: the widget tree above, the selected widget's sheet below.
|
# Workbench's layout: the widget tree above, the selected widget's sheet below.
|
||||||
side = ttk.PanedWindow(body, orient=tk.VERTICAL)
|
side = ttk.PanedWindow(body, orient=tk.VERTICAL)
|
||||||
body.add(side, weight=0)
|
body.add(side, weight=0)
|
||||||
|
self._body = body
|
||||||
|
self._side = side
|
||||||
|
|
||||||
tree_frame = ttk.Frame(side)
|
tree_frame = ttk.Frame(side)
|
||||||
self._build_widget_tree(tree_frame)
|
self._build_widget_tree(tree_frame)
|
||||||
side.add(tree_frame, weight=1)
|
side.add(tree_frame, weight=1)
|
||||||
|
|
||||||
self._props = OxWidgetPropertiesPanel(side, self._commit_props, self._repaint_all)
|
self._props = OxWidgetPropertiesPanel(side, self._commit_props, self._repaint_all)
|
||||||
|
self._props.set_editable(self._edit_var.get())
|
||||||
side.add(self._props.frame, weight=3)
|
side.add(self._props.frame, weight=3)
|
||||||
|
|
||||||
def _build_widget_tree(self, parent):
|
def _build_widget_tree(self, parent):
|
||||||
@@ -319,7 +328,7 @@ class OxPageViewPanel:
|
|||||||
self._painter = WidgetPainter(c, sx, sy, z, bg_colour)
|
self._painter = WidgetPainter(c, sx, sy, z, bg_colour)
|
||||||
self._painter.page_image(_get_prop(self.comp, "imageFileName", ""),
|
self._painter.page_image(_get_prop(self.comp, "imageFileName", ""),
|
||||||
self._screen_w, self._screen_h)
|
self._screen_w, self._screen_h)
|
||||||
if self._grid_var.get():
|
if self._edit_var.get():
|
||||||
self._draw_grid(sx, sy, _grid_colour(bg_colour))
|
self._draw_grid(sx, sy, _grid_colour(bg_colour))
|
||||||
|
|
||||||
if self._frame_image:
|
if self._frame_image:
|
||||||
@@ -364,10 +373,48 @@ class OxPageViewPanel:
|
|||||||
for gy in range(GRID_STEP, self._screen_h, GRID_STEP):
|
for gy in range(GRID_STEP, self._screen_h, GRID_STEP):
|
||||||
c.create_line(sx, sy + gy * z, right, sy + gy * z, fill=colour, tags=("grid",))
|
c.create_line(sx, sy + gy * z, right, sy + gy * z, fill=colour, tags=("grid",))
|
||||||
|
|
||||||
def _on_grid_toggled(self):
|
def _on_edit_toggled(self):
|
||||||
global show_grid
|
global edit_mode
|
||||||
show_grid = self._grid_var.get()
|
edit_mode = self._edit_var.get()
|
||||||
|
if not edit_mode:
|
||||||
|
self._props.flush_edits()
|
||||||
|
self._props.set_editable(edit_mode)
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
app_state.set_status("Page Editor: edit mode — the page can be changed."
|
||||||
|
if edit_mode else "Page Editor: view mode — nothing can be changed.")
|
||||||
|
|
||||||
|
def _on_snap_toggled(self):
|
||||||
|
global snap_to_grid
|
||||||
|
snap_to_grid = self._snap_var.get()
|
||||||
|
|
||||||
|
def _on_right_pane_toggled(self):
|
||||||
|
global show_right_pane
|
||||||
|
show_right_pane = self._right_pane_var.get()
|
||||||
|
self._apply_right_pane()
|
||||||
|
|
||||||
|
def _apply_right_pane(self):
|
||||||
|
"""Takes the widget tree and properties sheet out of the split, or puts them back."""
|
||||||
|
showing = str(self._side) in self._body.panes()
|
||||||
|
if self._right_pane_var.get() and not showing:
|
||||||
|
self._body.add(self._side, weight=0)
|
||||||
|
elif not self._right_pane_var.get() and showing:
|
||||||
|
self._props.flush_edits()
|
||||||
|
self._body.forget(self._side)
|
||||||
|
|
||||||
|
# ── main toolbar ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
def toolbar_tools(self):
|
||||||
|
"""The Page Editor's own toolbar buttons: (icon, tooltip, variable, command).
|
||||||
|
|
||||||
|
`main` lays them out on its toolbar while this editor is open and takes them
|
||||||
|
down with it. A button with a variable is a toggle; add new tools here.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
("✏", "Edit Mode", self._edit_var, self._on_edit_toggled),
|
||||||
|
("⌗", "Snap to Grid", self._snap_var, self._on_snap_toggled),
|
||||||
|
("◨", "Widgets / Properties Pane", self._right_pane_var,
|
||||||
|
self._on_right_pane_toggled),
|
||||||
|
]
|
||||||
|
|
||||||
def _draw_widget(self, widget):
|
def _draw_widget(self, widget):
|
||||||
"""Paints the widget as the device would, then lays an empty box over it.
|
"""Paints the widget as the device would, then lays an empty box over it.
|
||||||
@@ -526,9 +573,12 @@ class OxPageViewPanel:
|
|||||||
widget = self._widget_at(cx, cy)
|
widget = self._widget_at(cx, cy)
|
||||||
if widget is not None:
|
if widget is not None:
|
||||||
self._select_widget(widget)
|
self._select_widget(widget)
|
||||||
self._drag["tag"] = self._widget_items[widget]
|
if not self._edit_var.get():
|
||||||
self._drag["x"] = cx
|
self._drag["tag"] = None
|
||||||
self._drag["y"] = cy
|
return
|
||||||
|
box = self._widget_boxes[widget]
|
||||||
|
self._drag.update(tag=self._widget_items[widget], x=cx, y=cy,
|
||||||
|
origin=(box[0], box[1]), pos=(box[0], box[1]))
|
||||||
else:
|
else:
|
||||||
self._select_widget(None)
|
self._select_widget(None)
|
||||||
self._drag["tag"] = None
|
self._drag["tag"] = None
|
||||||
@@ -538,19 +588,27 @@ class OxPageViewPanel:
|
|||||||
return
|
return
|
||||||
|
|
||||||
c = self._canvas
|
c = self._canvas
|
||||||
|
z = self._zoom
|
||||||
cx = c.canvasx(event.x)
|
cx = c.canvasx(event.x)
|
||||||
cy = c.canvasy(event.y)
|
cy = c.canvasy(event.y)
|
||||||
|
|
||||||
dx = cx - self._drag["x"]
|
# Worked out from where the drag began rather than step by step, so snapping
|
||||||
dy = cy - self._drag["y"]
|
# never accumulates rounding and the widget lands exactly where it is drawn.
|
||||||
|
ox, oy = self._drag["origin"]
|
||||||
|
new_x = ox + (cx - self._drag["x"]) / z
|
||||||
|
new_y = oy + (cy - self._drag["y"]) / z
|
||||||
|
if self._snap_var.get():
|
||||||
|
new_x = round(new_x / GRID_STEP) * GRID_STEP
|
||||||
|
new_y = round(new_y / GRID_STEP) * GRID_STEP
|
||||||
|
new_x = int(round(max(0, min(new_x, self._screen_w))))
|
||||||
|
new_y = int(round(max(0, min(new_y, self._screen_h))))
|
||||||
|
|
||||||
tag = self._drag["tag"]
|
px, py = self._drag["pos"]
|
||||||
c.move(tag, dx, dy)
|
if (new_x, new_y) != (px, py):
|
||||||
|
c.move(self._drag["tag"], (new_x - px) * z, (new_y - py) * z)
|
||||||
|
self._drag["pos"] = (new_x, new_y)
|
||||||
c.delete("selection_handle")
|
c.delete("selection_handle")
|
||||||
|
|
||||||
self._drag["x"] = cx
|
|
||||||
self._drag["y"] = cy
|
|
||||||
|
|
||||||
def _on_canvas_release(self, event):
|
def _on_canvas_release(self, event):
|
||||||
tag = self._drag["tag"]
|
tag = self._drag["tag"]
|
||||||
if not tag:
|
if not tag:
|
||||||
@@ -562,17 +620,9 @@ class OxPageViewPanel:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._drag["tag"] = None
|
self._drag["tag"] = None
|
||||||
c = self._canvas
|
|
||||||
z = self._zoom
|
|
||||||
bbox = c.bbox(f"{tag} && ox_widget_rect")
|
|
||||||
moved = False
|
moved = False
|
||||||
if bbox:
|
new_x, new_y = self._drag["pos"]
|
||||||
new_x = int((bbox[0] - self._screen_canvas_x * z) / z)
|
if (new_x, new_y) != self._drag["origin"]:
|
||||||
new_y = int((bbox[1] - self._screen_canvas_y * z) / z)
|
|
||||||
|
|
||||||
new_x = max(0, min(new_x, self._screen_w))
|
|
||||||
new_y = max(0, min(new_y, self._screen_h))
|
|
||||||
|
|
||||||
moved = self._commit_props(widget, {"x": str(new_x), "y": str(new_y)})
|
moved = self._commit_props(widget, {"x": str(new_x), "y": str(new_y)})
|
||||||
|
|
||||||
# A commit re-renders through refresh(); a drag that went nowhere still has to
|
# A commit re-renders through refresh(); a drag that went nowhere still has to
|
||||||
|
|||||||
+47
-3
@@ -574,6 +574,10 @@ class OxWidgetPropertiesPanel:
|
|||||||
self.repaint = repaint
|
self.repaint = repaint
|
||||||
self.comp = None
|
self.comp = None
|
||||||
self.rows = []
|
self.rows = []
|
||||||
|
# Off until the Page Editor's edit mode is switched on: every field is then shown
|
||||||
|
# but none of them takes input, and no write reaches `commit`.
|
||||||
|
self.editable = False
|
||||||
|
self._controls = [] # (widget, state when editable) for every input on the sheet
|
||||||
self.frame = ttk.Frame(parent)
|
self.frame = ttk.Frame(parent)
|
||||||
|
|
||||||
self._name_font = tkfont.Font(family="Segoe UI", size=9)
|
self._name_font = tkfont.Font(family="Segoe UI", size=9)
|
||||||
@@ -637,6 +641,7 @@ class OxWidgetPropertiesPanel:
|
|||||||
for child in self._sheet.winfo_children():
|
for child in self._sheet.winfo_children():
|
||||||
child.destroy()
|
child.destroy()
|
||||||
self.rows = []
|
self.rows = []
|
||||||
|
self._controls = []
|
||||||
self._status_var.set("")
|
self._status_var.set("")
|
||||||
|
|
||||||
if self.comp is None:
|
if self.comp is None:
|
||||||
@@ -649,9 +654,36 @@ class OxWidgetPropertiesPanel:
|
|||||||
|
|
||||||
for grid_row, prop in enumerate(build_props(self.comp)):
|
for grid_row, prop in enumerate(build_props(self.comp)):
|
||||||
self.rows.append(self._build_row(grid_row, prop))
|
self.rows.append(self._build_row(grid_row, prop))
|
||||||
|
self._apply_editable()
|
||||||
self.reload()
|
self.reload()
|
||||||
self._canvas.yview_moveto(0)
|
self._canvas.yview_moveto(0)
|
||||||
|
|
||||||
|
def set_editable(self, editable):
|
||||||
|
"""Edit mode on or off: the fields take input, or only show their values."""
|
||||||
|
self.editable = editable
|
||||||
|
self._apply_editable()
|
||||||
|
|
||||||
|
def _apply_editable(self):
|
||||||
|
for widget, editable_state in self._controls:
|
||||||
|
state = editable_state if self.editable else "disabled"
|
||||||
|
# Combobox first: it is an Entry too, and "readonly" would still let it pick.
|
||||||
|
if isinstance(widget, ttk.Combobox):
|
||||||
|
widget.configure(state=state)
|
||||||
|
elif isinstance(widget, tk.Entry):
|
||||||
|
widget.configure(state="normal" if self.editable else "readonly")
|
||||||
|
else:
|
||||||
|
widget.state(["!disabled"] if self.editable else ["disabled"])
|
||||||
|
if self.comp is not None:
|
||||||
|
self._status_var.set("" if self.editable else
|
||||||
|
"View mode — switch on edit mode (✏) to change values.")
|
||||||
|
|
||||||
|
def _guarded(self, command):
|
||||||
|
"""`command`, run only while the sheet is editable."""
|
||||||
|
def run(*args):
|
||||||
|
if self.editable:
|
||||||
|
return command(*args)
|
||||||
|
return run
|
||||||
|
|
||||||
def flush_edits(self):
|
def flush_edits(self):
|
||||||
"""Commits a typed value still sitting in its field.
|
"""Commits a typed value still sitting in its field.
|
||||||
|
|
||||||
@@ -695,7 +727,9 @@ class OxWidgetPropertiesPanel:
|
|||||||
return label
|
return label
|
||||||
|
|
||||||
def _dots(self, cell, command):
|
def _dots(self, cell, command):
|
||||||
ttk.Button(cell, text="…", width=3, command=command).pack(side=tk.RIGHT, padx=1)
|
button = ttk.Button(cell, text="…", width=3, command=self._guarded(command))
|
||||||
|
button.pack(side=tk.RIGHT, padx=1)
|
||||||
|
self._controls.append((button, None))
|
||||||
|
|
||||||
def _build_readonly(self, cell, row):
|
def _build_readonly(self, cell, row):
|
||||||
self._value_label(cell, row, READONLY_FG)
|
self._value_label(cell, row, READONLY_FG)
|
||||||
@@ -712,6 +746,10 @@ class OxWidgetPropertiesPanel:
|
|||||||
|
|
||||||
def commit(_event=None):
|
def commit(_event=None):
|
||||||
text = var.get()
|
text = var.get()
|
||||||
|
if not self.editable:
|
||||||
|
if text != row["loaded"]:
|
||||||
|
var.set(row["loaded"])
|
||||||
|
return
|
||||||
if text != row["loaded"]:
|
if text != row["loaded"]:
|
||||||
# Taken as loaded first: the write repaints, and the repaint must not
|
# Taken as loaded first: the write repaints, and the repaint must not
|
||||||
# find this same edit still pending and commit it a second time.
|
# find this same edit still pending and commit it a second time.
|
||||||
@@ -739,6 +777,7 @@ class OxWidgetPropertiesPanel:
|
|||||||
highlightbackground=SHEET_BG)
|
highlightbackground=SHEET_BG)
|
||||||
entry.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2, pady=1)
|
entry.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=2, pady=1)
|
||||||
self._bind_commit(entry, row)
|
self._bind_commit(entry, row)
|
||||||
|
self._controls.append((entry, None))
|
||||||
|
|
||||||
def _build_image(self, cell, row):
|
def _build_image(self, cell, row):
|
||||||
combo = ttk.Combobox(cell, textvariable=row["var"], font=self._name_font,
|
combo = ttk.Combobox(cell, textvariable=row["var"], font=self._name_font,
|
||||||
@@ -746,6 +785,7 @@ class OxWidgetPropertiesPanel:
|
|||||||
combo.pack(fill=tk.BOTH, expand=True, padx=1)
|
combo.pack(fill=tk.BOTH, expand=True, padx=1)
|
||||||
commit = self._bind_commit(combo, row)
|
commit = self._bind_commit(combo, row)
|
||||||
combo.bind("<<ComboboxSelected>>", commit)
|
combo.bind("<<ComboboxSelected>>", commit)
|
||||||
|
self._controls.append((combo, "normal"))
|
||||||
|
|
||||||
def _build_choice(self, cell, row):
|
def _build_choice(self, cell, row):
|
||||||
prop, var = row["prop"], row["var"]
|
prop, var = row["prop"], row["var"]
|
||||||
@@ -759,6 +799,7 @@ class OxWidgetPropertiesPanel:
|
|||||||
|
|
||||||
combo.bind("<<ComboboxSelected>>", selected)
|
combo.bind("<<ComboboxSelected>>", selected)
|
||||||
row["load"] = lambda: var.set(prop.read())
|
row["load"] = lambda: var.set(prop.read())
|
||||||
|
self._controls.append((combo, "readonly"))
|
||||||
|
|
||||||
def _build_colour(self, cell, row):
|
def _build_colour(self, cell, row):
|
||||||
prop, var = row["prop"], row["var"]
|
prop, var = row["prop"], row["var"]
|
||||||
@@ -781,14 +822,14 @@ class OxWidgetPropertiesPanel:
|
|||||||
|
|
||||||
self._dots(cell, choose)
|
self._dots(cell, choose)
|
||||||
self._value_label(cell, row, READONLY_FG)
|
self._value_label(cell, row, READONLY_FG)
|
||||||
swatch.bind("<Button-1>", choose)
|
swatch.bind("<Button-1>", self._guarded(choose))
|
||||||
row["load"] = load
|
row["load"] = load
|
||||||
|
|
||||||
def _build_dialog(self, cell, row):
|
def _build_dialog(self, cell, row):
|
||||||
opener = getattr(self, f"_open_{row['prop'].dialog}_dialog")
|
opener = getattr(self, f"_open_{row['prop'].dialog}_dialog")
|
||||||
self._dots(cell, opener)
|
self._dots(cell, opener)
|
||||||
label = self._value_label(cell, row)
|
label = self._value_label(cell, row)
|
||||||
label.bind("<Double-Button-1>", lambda e: opener())
|
label.bind("<Double-Button-1>", self._guarded(lambda e: opener()))
|
||||||
row["load"] = lambda: row["var"].set(row["prop"].read())
|
row["load"] = lambda: row["var"].set(row["prop"].read())
|
||||||
|
|
||||||
def _build_link(self, cell, row):
|
def _build_link(self, cell, row):
|
||||||
@@ -815,6 +856,9 @@ class OxWidgetPropertiesPanel:
|
|||||||
# ── writing ───────────────────────────────────────────────────
|
# ── writing ───────────────────────────────────────────────────
|
||||||
|
|
||||||
def _write(self, row, text):
|
def _write(self, row, text):
|
||||||
|
if not self.editable:
|
||||||
|
row["load"]()
|
||||||
|
return
|
||||||
values, error = row["prop"].write(text)
|
values, error = row["prop"].write(text)
|
||||||
if error:
|
if error:
|
||||||
self._status_var.set(f"{row['prop'].label}: {error}")
|
self._status_var.set(f"{row['prop'].label}: {error}")
|
||||||
|
|||||||
Reference in New Issue
Block a user