Properties view: every slot of a component, edited in the editor pane
Right-click a component on the wiresheet or in the tree and Properties takes
over the editor pane, listing every slot the manifest declares rather than the
handful a box paints. Config slots that are not @readonly are white and
editable; everything else is grey.
Rows are built from the slot's facets, read against SedonaDoc/niagara.html:
@trueText/@falseText, @range, @precision, @unit, and @min/@max, which bound
what Save accepts. Three facets it had wrong:
- null is a value, not an absence. control::WriteFloat pairs 'in' with a
setNull action and the sample app stores val="null" on four of them, so
null now shows, parses back, and reads as @nullText where declared.
- @min/@max on a Buf count characters. TcpDevice.ipAddr is a Buf(17) with
@max 16; a longer string is refused rather than overrunning the device.
- an int's implicit range is noise. [-2147483648 - 2147483647] said no more
than "a whole number", so only byte and short print a range they did not
declare.
meta gets a row of its own: the '>>' opens its user groups as checkboxes and
its position as x and y, since typing 822411269 into a field sets neither.
Save writes only the rows that moved, as one undoable SlotValueCommand, and
refuses the lot if any row fails its range check. An edited field paints the
view in the canvas's own dirty tint, and every way of leaving asks before
discarding.
The file name on the tree's root row goes red while anything is unwritten,
counting both a dirty document and unsaved fields. Two things this needed:
the nav tree runs on its own style, because a themed Treeview's selected
foreground beats a row tag's; and the tag is added to or removed from the row
rather than recoloured, because reconfiguring a tag does not repaint rows that
are already drawn.
Also fixes a latent hazard the panel exposed: Del and Backspace are bound
application-wide, so they now stand down while a text field has focus rather
than deleting the component behind it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+42
-3
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
|
||||
import configparser
|
||||
import os
|
||||
|
||||
VERSION = "0.0.0.036"
|
||||
VERSION = "0.0.0.048"
|
||||
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
||||
DEFAULT_GRID_SIZE = 10
|
||||
DEFAULT_ADD_TIMESTAMP = True
|
||||
@@ -26,12 +26,43 @@ DEFAULT_LEFT_PANE_WIDTH = 230
|
||||
MIN_ALLOWED_LEFT_PANE_WIDTH = 120
|
||||
MAX_ALLOWED_LEFT_PANE_WIDTH = 600
|
||||
|
||||
# Called whenever "is there anything unwritten" may have changed, so the file name on
|
||||
# the tree can follow it. main registers it; anything before that is a no-op.
|
||||
dirty_display_callback = None
|
||||
|
||||
# Widget handles, filled in by main once the UI exists
|
||||
window = None
|
||||
canvas = None
|
||||
tree = None
|
||||
status_label = None # the window's bottom status bar
|
||||
|
||||
# The unsaved-state tint. The wiresheet paints it on the canvas and the Properties view
|
||||
# paints it on itself, so "there is something here you have not written yet" is one
|
||||
# colour wherever the change was made.
|
||||
DIRTY_TINT = "#ffe6e6"
|
||||
CLEAN_TINT = "white"
|
||||
|
||||
# The file name on the tree's root row turns this colour while anything is unwritten,
|
||||
# which is the one place a file name appears on screen -- a Tk window title cannot be
|
||||
# coloured.
|
||||
#
|
||||
# The tag's colour is configured once and never changed; what moves is whether the row
|
||||
# carries the tag. Reconfiguring a tag does not reliably repaint rows that are already
|
||||
# drawn -- which is why the name only ever went red on the paths that happened to rebuild
|
||||
# the tree, and stayed black after a box was dragged. Setting a row's tags repaints that
|
||||
# row, so the two states cannot drift apart.
|
||||
DIRTY_NAME_COLOUR = "#c00000"
|
||||
DIRTY_NAME_TAG = "unsaved_file"
|
||||
|
||||
# A themed Treeview maps the selected state to SystemHighlightText, and that mapping beats
|
||||
# a row tag's own foreground -- so the file name turned white-on-blue the moment its row
|
||||
# was selected, which is exactly when the root sheet is the one being looked at. The
|
||||
# navigation tree therefore runs on a style of its own that drops the selected foreground
|
||||
# and picks a light selection background instead, so a tag's colour shows in every state
|
||||
# and black text still reads on the highlight.
|
||||
NAV_TREE_STYLE = "Nav.Treeview"
|
||||
NAV_SELECTION_BG = "#cce4f7"
|
||||
|
||||
# Status bar tones. A hint that something needs attention later is worth a colour, so a
|
||||
# placement that leaves the schema out of step is not read as a plain confirmation.
|
||||
STATUS_INFO_COLOUR = "#333333"
|
||||
@@ -148,13 +179,20 @@ def clear_status():
|
||||
set_status("")
|
||||
|
||||
|
||||
def refresh_dirty_display():
|
||||
"""Nudges whatever shows 'there is something unwritten'. A no-op before the UI exists."""
|
||||
if dirty_display_callback is not None:
|
||||
dirty_display_callback()
|
||||
|
||||
|
||||
def mark_dirty():
|
||||
"""Single funnel for 'the document changed', including the unsaved-state tint."""
|
||||
global is_dirty
|
||||
if not is_dirty:
|
||||
is_dirty = True
|
||||
if canvas is not None:
|
||||
canvas.config(bg="#ffe6e6")
|
||||
canvas.config(bg=DIRTY_TINT)
|
||||
refresh_dirty_display()
|
||||
|
||||
|
||||
def mark_clean():
|
||||
@@ -162,7 +200,8 @@ def mark_clean():
|
||||
global is_dirty
|
||||
is_dirty = False
|
||||
if canvas is not None:
|
||||
canvas.config(bg="white")
|
||||
canvas.config(bg=CLEAN_TINT)
|
||||
refresh_dirty_display()
|
||||
|
||||
|
||||
SEDONA_HOME, GRID_SIZE, ADD_TIMESTAMP, BOX_WIDTH, LEFT_PANE_WIDTH = load_preferences()
|
||||
|
||||
Reference in New Issue
Block a user