Skip to content

Commit

Permalink
Reapply #2434
Browse files Browse the repository at this point in the history
There should be a way to make this work safely.
  • Loading branch information
amolenaar committed Sep 16, 2023
1 parent f86bd46 commit 2f209d5
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 116 deletions.
173 changes: 130 additions & 43 deletions gaphor/UML/actions/activitypropertypage.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from gi.repository import Gtk
from __future__ import annotations

from gi.repository import GObject, Gio, Gdk, Gtk

from gaphor import UML
from gaphor.core import transactional
from gaphor.core import transactional, event_handler
from gaphor.core.format import format, parse
from gaphor.core.modeling import AssociationUpdated
from gaphor.diagram.propertypages import (
EditableTreeModel,
PropertyPageBase,
PropertyPages,
help_link,
Expand All @@ -14,47 +16,66 @@
on_text_cell_edited,
unsubscribe_all_on_destroy,
)
from gaphor.i18n import translated_ui_string
from gaphor.UML.actions.activity import ActivityItem
from gaphor.UML.classes.classespropertypages import on_keypress_event

new_builder = new_resource_builder("gaphor.UML.actions")

new_builder = new_resource_builder("gaphor.UML.actions")

class ActivityParameters(EditableTreeModel):
"""GTK tree model to edit class attributes."""

def __init__(self, item):
super().__init__(item, cols=(str, object))
class ActivityParameterNodeView(GObject.Object):
__gtype_name__ = "ActivityParameterNodeView"

def get_rows(self):
for node in self._item.subject.node:
if isinstance(node, UML.ActivityParameterNode):
yield [format(node.parameter), node]
def __init__(self, node: UML.ActivityParameterNode | None, activity: UML.Activity):
super().__init__()
self.node = node
self.activity = activity

def create_object(self):
model = self._item.model
node = model.create(UML.ActivityParameterNode)
node.parameter = model.create(UML.Parameter)
self._item.subject.node = node
return node
@GObject.Property(type=str)
def parameter(self) -> str:
return format(self.node.parameter) if self.node else ""

@parameter.setter # type: ignore[no-redef]
@transactional
def set_object_value(self, row, col, value):
node = row[-1]
if col == 0:
parse(node.parameter, value)
row[0] = format(node.parameter)
elif col == 1:
# Value in attribute object changed:
row[0] = format(node.parameter)

def swap_objects(self, o1, o2):
return self._item.subject.node.swap(o1, o2)

def sync_model(self, new_order):
self._item.subject.node.order(
lambda key: new_order.index(key) if key in new_order else -1
)
def parameter(self, value):
if not self.node:
model = self.activity.model
node = model.create(UML.ActivityParameterNode)
node.parameter = model.create(UML.Parameter)
self.node = node
self.activity.node = node
parse(self.node.parameter, value)


def activity_parameter_node_model(activity: UML.Activity) -> Gio.ListModel:
store = Gio.ListStore.new(ActivityParameterNodeView)

for node in activity.node:
if isinstance(node, UML.ActivityParameterNode) and node.parameter:
store.append(ActivityParameterNodeView(node, activity))
store.append(ActivityParameterNodeView(None, activity))

return store


def update_activity_parameter_node_model(
store: Gio.ListStore, activity: UML.Activity
) -> None:
n = 0
for node in activity.node:
if node is not store.get_item(n).node:
store.remove(n)
store.insert(n, ActivityParameterNodeView(node, activity))
n += 1

while store.get_n_items() > n:
store.remove(store.get_n_items() - 1)

if (
not store.get_n_items()
or store.get_item(store.get_n_items() - 1).node is not None
):
store.append(ActivityParameterNodeView(None, activity))


@PropertyPages.register(ActivityItem)
Expand All @@ -71,34 +92,99 @@ def construct(self):
if not subject:
return

self.model = ActivityParameters(self.item)

builder = new_builder(
"activity-editor",
"parameters-info",
signals={
"parameter-edited": (on_text_cell_edited, self.model, 0),
"parameters-info-clicked": (self.on_parameters_info_clicked),
},
)

self.info = builder.get_object("parameters-info")
help_link(builder, "parameters-info-icon", "parameters-info")

tree_view: Gtk.TreeView = builder.get_object("parameter-list")
tree_view.set_model(self.model)
controller = Gtk.EventControllerKey.new()
tree_view.add_controller(controller)
controller.connect("key-pressed", on_keypress_event, tree_view)
list_view: Gtk.ListView = builder.get_object("parameter-list")

self.model = activity_parameter_node_model(subject)
selection = Gtk.SingleSelection.new(self.model)
list_view.set_model(selection)
list_view.add_controller(keyboard_shortcuts(selection))

factory = Gtk.SignalListItemFactory.new()
factory.connect(
"setup",
list_item_factory_setup,
)

list_view.set_factory(factory)

if self.watcher:
self.watcher.watch("node", self.on_nodes_changed)

return unsubscribe_all_on_destroy(
builder.get_object("activity-editor"), self.watcher
)

@event_handler(AssociationUpdated)
def on_nodes_changed(self, event):
update_activity_parameter_node_model(self.model, self.item.subject)

def on_parameters_info_clicked(self, image, event):
self.info.set_visible(True)


def list_item_factory_setup(_factory, list_item):
builder = Gtk.Builder()
builder.set_current_object(list_item)
builder.extend_with_template(
list_item,
type(list_item).__gtype__,
translated_ui_string("gaphor.UML.actions", "parameter.ui"),
-1,
)

def end_editing(text, pspec):
if not text.props.editing:
list_item.get_item().parameter = text.get_text()

text = builder.get_object("text")
text.connect("notify::editing", end_editing)


def keyboard_shortcuts(selection):
ctrl = Gtk.EventControllerKey.new()
ctrl.connect("key-pressed", list_view_handler, selection)
return ctrl


@transactional
def list_view_handler(_list_view, keyval, _keycode, state, selection):
item = selection.get_selected_item()
if not (item and item.node):
return False

if keyval in (Gdk.KEY_Delete, Gdk.KEY_BackSpace) and not state & (
Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK
):
item.node.unlink()
return True

elif keyval in (Gdk.KEY_equal, Gdk.KEY_plus, Gdk.KEY_minus, Gdk.KEY_underscore):
pos = selection.get_selected()
swap_pos = pos + 1 if keyval in (Gdk.KEY_equal, Gdk.KEY_plus) else pos - 1
if not 0 <= swap_pos < selection.get_n_items():
return False

other = selection.get_item(swap_pos)
if not (other and other.node):
return False

if item.activity.node.swap(item.node, other.node):
selection.set_selected(swap_pos)
return True
return False


@PropertyPages.register(UML.ActivityParameterNode)
class ActivityParameterNodeNamePropertyPage(PropertyPageBase):
"""An adapter which works for any named item view.
Expand Down Expand Up @@ -143,3 +229,4 @@ def handler(event):
def _on_name_changed(self, entry):
if self.subject.parameter.name != entry.get_text():
self.subject.parameter.name = entry.get_text()

18 changes: 18 additions & 0 deletions gaphor/UML/actions/parameter.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version='1.0' encoding='UTF-8'?>
<interface>
<requires lib="gtk" version="4.6"/>
<template class="GtkListItem">
<property name="child">
<object class="GtkEditableLabel" id="text">
<style>
<class name="row"/>
</style>
<binding name="text">
<lookup name="parameter" type="ActivityParameterNodeView">
<lookup name="item">GtkListItem</lookup>
</lookup>
</binding>
</object>
</property>
</template>
</interface>
28 changes: 1 addition & 27 deletions gaphor/UML/actions/propertypages.ui
Original file line number Diff line number Diff line change
Expand Up @@ -304,35 +304,9 @@
<child>
<object class="GtkFrame">
<property name="child">
<object class="GtkTreeView" id="parameter-list">
<object class="GtkListView" id="parameter-list">
<property name="height-request">112</property>
<property name="focusable">1</property>
<property name="headers-clickable">0</property>
<property name="enable-search">0</property>
<property name="show-expanders">0</property>
<property name="enable-grid-lines">horizontal</property>
<child internal-child="selection">
<object class="GtkTreeSelection" />
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="resizable">1</property>
<property name="title" translatable="yes">Parameters</property>
<property name="expand">1</property>
<child>
<object class="GtkCellRendererText">
<property name="xpad">2</property>
<property name="ypad">2</property>
<property name="xalign">0</property>
<property name="editable">1</property>
<signal name="edited" handler="parameter-edited" swapped="no" />
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
</property>
</object>
Expand Down
Loading

0 comments on commit 2f209d5

Please sign in to comment.