Tree rows get the same right-click actions as canvas boxes
Right-clicking a component in the navigation tree offered Delete Component alone, while the same component on the wiresheet also offered Link Mark and Link From / Link To. Which list a component was picked from should not change what can be done with it, so both popups are now built by fill_component_popup. Its entries close over the component instead of reading a selection back, which is what lets each popup act on the row or box that was actually clicked -- the canvas commands went through app_state.selected_canvas_tag, which a tree row never sets. do_link_mark, do_link_from_mark, do_link_to_mark and delete_tree_selected_component fall out with it, and Link Mark now says in the status bar what it marked, since nothing else on screen shows that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -128,6 +128,14 @@ element has left the document, keeping the position on screen. Walking the histo
|
|||||||
the tree selection, which is what paints the sheet, and is suppressed from recording so a
|
the tree selection, which is what paints the sheet, and is suppressed from recording so a
|
||||||
walk cannot rewrite the trail it is following.
|
walk cannot rewrite the trail it is following.
|
||||||
|
|
||||||
|
### The component popup
|
||||||
|
|
||||||
|
Right-clicking a component offers the same actions from the wiresheet and from the
|
||||||
|
navigation tree -- Delete Component, Link Mark, and Link From / Link To naming the marked
|
||||||
|
component once one is marked. `fill_component_popup` builds both, and its entries close
|
||||||
|
over the component rather than reading a selection back, which is what lets each popup act
|
||||||
|
on the row or box that was actually clicked. The tree's root row keeps its own popup.
|
||||||
|
|
||||||
### Following a link to its other sheet
|
### Following a link to its other sheet
|
||||||
|
|
||||||
Right-clicking a wire or a knob offers **Go to Link Wire Sheet "<name>"** alongside Delete
|
Right-clicking a wire or a knob offers **Go to Link Wire Sheet "<name>"** alongside Delete
|
||||||
|
|||||||
+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.035"
|
VERSION = "0.0.0.036"
|
||||||
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
|
||||||
|
|||||||
@@ -392,16 +392,6 @@ def delete_selected_component(event=None):
|
|||||||
sax_file.execute_cascading_deletion(comp_element)
|
sax_file.execute_cascading_deletion(comp_element)
|
||||||
|
|
||||||
|
|
||||||
def delete_tree_selected_component():
|
|
||||||
"""Triggered by the context popup menu to delete an item selected in the Treeview architecture."""
|
|
||||||
if not app_state.selected_tree_item_id:
|
|
||||||
return
|
|
||||||
|
|
||||||
comp_element = app_state.tree_element_map.get(app_state.selected_tree_item_id)
|
|
||||||
if comp_element is not None:
|
|
||||||
sax_file.execute_cascading_deletion(comp_element)
|
|
||||||
|
|
||||||
|
|
||||||
def on_tree_right_click(event):
|
def on_tree_right_click(event):
|
||||||
"""Intercepts right-clicks on the treeview architecture to render branch context panels."""
|
"""Intercepts right-clicks on the treeview architecture to render branch context panels."""
|
||||||
tree = app_state.tree
|
tree = app_state.tree
|
||||||
@@ -415,8 +405,16 @@ def on_tree_right_click(event):
|
|||||||
|
|
||||||
if parent_id == "":
|
if parent_id == "":
|
||||||
tree_root_context_menu.post(event.x_root, event.y_root)
|
tree_root_context_menu.post(event.x_root, event.y_root)
|
||||||
else:
|
return
|
||||||
tree_comp_context_menu.post(event.x_root, event.y_root)
|
|
||||||
|
comp_element = app_state.tree_element_map.get(item_id)
|
||||||
|
if comp_element is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
# The same actions the canvas offers, on the component the tree row names: which
|
||||||
|
# list a component was picked from should not change what can be done with it.
|
||||||
|
fill_component_popup(tree_comp_context_menu, comp_element)
|
||||||
|
tree_comp_context_menu.post(event.x_root, event.y_root)
|
||||||
|
|
||||||
|
|
||||||
def on_canvas_click(event):
|
def on_canvas_click(event):
|
||||||
@@ -464,13 +462,14 @@ def on_canvas_click(event):
|
|||||||
wiresheet.redraw_links()
|
wiresheet.redraw_links()
|
||||||
|
|
||||||
|
|
||||||
def do_link_mark():
|
def set_link_mark(comp):
|
||||||
"""Marks the currently selected canvas component as a link endpoint."""
|
"""Marks a component as one end of a link still to be made."""
|
||||||
if not app_state.selected_canvas_tag:
|
if comp is None:
|
||||||
return
|
return
|
||||||
comp = app_state.canvas_comp_map.get(app_state.selected_canvas_tag)
|
app_state.link_mark_element = comp
|
||||||
if comp is not None:
|
app_state.set_status(
|
||||||
app_state.link_mark_element = comp
|
f"Marked '{comp.get('name', '?')}' — right-click the other component to link it."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _slot_linkable(slot_flags, slot_type, readonly, is_target):
|
def _slot_linkable(slot_flags, slot_type, readonly, is_target):
|
||||||
@@ -668,22 +667,33 @@ def open_link_dialog(source_element, target_element):
|
|||||||
ttk.Button(btn_frame, text="Cancel", width=10, command=dialog.destroy).pack(side=tk.LEFT, padx=5)
|
ttk.Button(btn_frame, text="Cancel", width=10, command=dialog.destroy).pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
|
||||||
def do_link_from_mark():
|
def fill_component_popup(menu, comp):
|
||||||
"""Link From: marked component is source, right-clicked component is target."""
|
"""Fills a popup with the actions that apply to one component.
|
||||||
if app_state.link_mark_element is None or not app_state.selected_canvas_tag:
|
|
||||||
return
|
|
||||||
target = app_state.canvas_comp_map.get(app_state.selected_canvas_tag)
|
|
||||||
if target is not None:
|
|
||||||
open_link_dialog(app_state.link_mark_element, target)
|
|
||||||
|
|
||||||
|
Shared by the canvas and the navigation tree, so a component offers the same
|
||||||
|
actions wherever it was right-clicked. The entries close over the component rather
|
||||||
|
than reading a selection back, which is what lets the two popups act on the thing
|
||||||
|
that was actually clicked.
|
||||||
|
"""
|
||||||
|
menu.delete(0, tk.END)
|
||||||
|
menu.add_command(
|
||||||
|
label="Delete Component",
|
||||||
|
command=lambda: sax_file.execute_cascading_deletion(comp),
|
||||||
|
)
|
||||||
|
menu.add_separator()
|
||||||
|
menu.add_command(label="Link Mark", command=lambda: set_link_mark(comp))
|
||||||
|
|
||||||
def do_link_to_mark():
|
marked = app_state.link_mark_element
|
||||||
"""Link To: right-clicked component is source, marked component is target."""
|
if marked is not None:
|
||||||
if app_state.link_mark_element is None or not app_state.selected_canvas_tag:
|
mark_name = marked.get("name", "?")
|
||||||
return
|
menu.add_command(
|
||||||
source = app_state.canvas_comp_map.get(app_state.selected_canvas_tag)
|
label=f'Link From "{mark_name}"',
|
||||||
if source is not None:
|
command=lambda: open_link_dialog(marked, comp),
|
||||||
open_link_dialog(source, app_state.link_mark_element)
|
)
|
||||||
|
menu.add_command(
|
||||||
|
label=f'Link To "{mark_name}"',
|
||||||
|
command=lambda: open_link_dialog(comp, marked),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def on_canvas_right_click(event):
|
def on_canvas_right_click(event):
|
||||||
@@ -710,8 +720,10 @@ def on_canvas_right_click(event):
|
|||||||
app_state.selected_canvas_tag = group_tag
|
app_state.selected_canvas_tag = group_tag
|
||||||
canvas.itemconfig(f"{group_tag} && block_outline", outline="#1f4e79", width=3)
|
canvas.itemconfig(f"{group_tag} && block_outline", outline="#1f4e79", width=3)
|
||||||
|
|
||||||
_rebuild_canvas_context_menu()
|
comp_element = app_state.canvas_comp_map.get(group_tag)
|
||||||
canvas_context_menu.post(event.x_root, event.y_root)
|
if comp_element is not None:
|
||||||
|
fill_component_popup(canvas_context_menu, comp_element)
|
||||||
|
canvas_context_menu.post(event.x_root, event.y_root)
|
||||||
|
|
||||||
|
|
||||||
def on_canvas_drag(event):
|
def on_canvas_drag(event):
|
||||||
@@ -1381,24 +1393,7 @@ def _rebuild_link_context_menu():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _rebuild_canvas_context_menu():
|
|
||||||
"""Rebuilds the canvas context menu with dynamic Link From/Link To labels."""
|
|
||||||
canvas_context_menu.delete(0, tk.END)
|
|
||||||
canvas_context_menu.add_command(label="Delete Component", command=delete_selected_component)
|
|
||||||
canvas_context_menu.add_separator()
|
|
||||||
canvas_context_menu.add_command(label="Link Mark", command=do_link_mark)
|
|
||||||
|
|
||||||
if app_state.link_mark_element is not None:
|
|
||||||
mark_name = app_state.link_mark_element.get("name", "?")
|
|
||||||
canvas_context_menu.add_command(
|
|
||||||
label=f'Link From "{mark_name}"', command=do_link_from_mark
|
|
||||||
)
|
|
||||||
canvas_context_menu.add_command(
|
|
||||||
label=f'Link To "{mark_name}"', command=do_link_to_mark
|
|
||||||
)
|
|
||||||
|
|
||||||
tree_comp_context_menu = tk.Menu(window, tearoff=0)
|
tree_comp_context_menu = tk.Menu(window, tearoff=0)
|
||||||
tree_comp_context_menu.add_command(label="Delete Component", command=delete_tree_selected_component)
|
|
||||||
|
|
||||||
tree_root_context_menu = tk.Menu(window, tearoff=0)
|
tree_root_context_menu = tk.Menu(window, tearoff=0)
|
||||||
tree_root_context_menu.add_command(label="Save", command=sax_file.execute_direct_overwrite_save)
|
tree_root_context_menu.add_command(label="Save", command=sax_file.execute_direct_overwrite_save)
|
||||||
|
|||||||
Reference in New Issue
Block a user