Skip to content

Commit

Permalink
Fix #1001. Splitting multiple panels now works.
Browse files Browse the repository at this point in the history
  • Loading branch information
cmeyer committed Feb 21, 2024
1 parent dcb8bf4 commit 7460af8
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 85 deletions.
86 changes: 38 additions & 48 deletions nion/swift/DocumentController.py
Original file line number Diff line number Diff line change
Expand Up @@ -2682,6 +2682,7 @@ def __init__(self,
focus_widget: typing.Optional[UserInterface.Widget],
display_panel: typing.Optional[DisplayPanel.DisplayPanel],
display_panels: typing.List[DisplayPanel.DisplayPanel],
selected_display_panel: typing.Optional[DisplayPanel.DisplayPanel],
model: DocumentModel.DocumentModel,
display_item: typing.Optional[DisplayItem.DisplayItem],
display_items: typing.Sequence[DisplayItem.DisplayItem],
Expand All @@ -2692,6 +2693,7 @@ def __init__(self,
super().__init__(application, window, focus_widget)
self.display_panel = display_panel
self.display_panels = display_panels
self.selected_display_panel = selected_display_panel
self.model = model
self.display_item = display_item
self.display_items = display_items
Expand All @@ -2702,6 +2704,7 @@ def __init__(self,

def _get_action_context(self) -> ActionContext:
focus_widget = self.focus_widget
selected_display_panel = self.selected_display_panel
display_panel = self.selected_display_panel if not self.__secondary_display_panels else None
display_panels = ([self.__selected_display_panel] if self.__selected_display_panel else list()) + self.__secondary_display_panels
model = self.document_model
Expand All @@ -2716,8 +2719,9 @@ def _get_action_context(self) -> ActionContext:
data_items.append(data_item_1)
selected_graphics = display_item.selected_graphics if display_item else list()
return DocumentController.ActionContext(typing.cast("Application.Application", self.app), self, focus_widget,
display_panel, display_panels, model, display_item, display_items,
crop_graphic, selected_graphics, data_item, data_items)
display_panel, display_panels, selected_display_panel, model,
display_item, display_items, crop_graphic, selected_graphics, data_item,
data_items)

def _get_action_context_for_display_items(self, display_items: typing.Sequence[DisplayItem.DisplayItem], display_panel: typing.Optional[DisplayPanel.DisplayPanel]) -> ActionContext:
focus_widget = self.focus_widget
Expand All @@ -2726,6 +2730,7 @@ def _get_action_context_for_display_items(self, display_items: typing.Sequence[D
# the one that was context clicked. if multiple display panels are selected and the user context clicks on one
# of the selected ones, use the selected ones. if multiple display panels are selected and the user context
# clicks on an unselected one, then there is no display panel selected.
selected_display_panel = self.__selected_display_panel
used_display_panel = None
used_display_panels: typing.List[DisplayPanel.DisplayPanel] = list()
if display_panel:
Expand Down Expand Up @@ -2756,9 +2761,9 @@ def _get_action_context_for_display_items(self, display_items: typing.Sequence[D
used_data_items.append(data_item_1)
selected_graphics = used_display_item.selected_graphics if used_display_item else list()
return DocumentController.ActionContext(typing.cast("Application.Application", self.app), self, focus_widget,
used_display_panel, used_display_panels, model, used_display_item,
used_display_items, crop_graphic, selected_graphics, used_data_item,
used_data_items)
used_display_panel, used_display_panels, selected_display_panel, model,
used_display_item, used_display_items, crop_graphic, selected_graphics,
used_data_item, used_data_items)


class GraphicFactoryBase:
Expand Down Expand Up @@ -3483,46 +3488,6 @@ def execute(self, context: Window.ActionContext) -> Window.ActionResult:
raise ValueError("Missing workspace controller")


class WorkspaceSplitHorizontalAction(Window.Action):
action_id = "workspace.split_horizontal"
action_name = _("Split Panel Into Left and Right")
action_command_icon_png = pkgutil.get_data(__name__, "resources/workspace_split_vertical.png")

def execute(self, context: Window.ActionContext) -> Window.ActionResult:
context = typing.cast(DocumentController.ActionContext, context)
window = typing.cast(DocumentController, context.window)
workspace_controller = window.workspace_controller
display_panel = context.display_panel
if workspace_controller and display_panel:
command = workspace_controller.insert_display_panel(display_panel, "right")
window.push_undo_command(command)
return Window.ActionResult(Window.ActionStatus.FINISHED)

def is_enabled(self, context: Window.ActionContext) -> bool:
context = typing.cast(DocumentController.ActionContext, context)
return context.display_panel is not None


class WorkspaceSplitVerticalAction(Window.Action):
action_id = "workspace.split_vertical"
action_name = _("Split Panel Into Top and Bottom")
action_command_icon_png = pkgutil.get_data(__name__, "resources/workspace_split_horizontal.png")

def execute(self, context: Window.ActionContext) -> Window.ActionResult:
context = typing.cast(DocumentController.ActionContext, context)
window = typing.cast(DocumentController, context.window)
workspace_controller = window.workspace_controller
display_panel = context.display_panel
if workspace_controller and display_panel:
command = workspace_controller.insert_display_panel(display_panel, "bottom")
window.push_undo_command(command)
return Window.ActionResult(Window.ActionStatus.FINISHED)

def is_enabled(self, context: Window.ActionContext) -> bool:
context = typing.cast(DocumentController.ActionContext, context)
return context.display_panel is not None


class WorkspaceSplitAction(Window.Action):
action_id = "workspace.split"
action_name = _("Split Display Panel")
Expand All @@ -3535,13 +3500,14 @@ def execute(self, context: Window.ActionContext) -> Window.ActionResult:
context = typing.cast(DocumentController.ActionContext, context)
window = typing.cast(DocumentController, context.window)
workspace_controller = window.workspace_controller
display_panel = context.display_panel
if workspace_controller and display_panel:
display_panels = context.display_panels
selected_display_panel = context.selected_display_panel
if workspace_controller and display_panels and selected_display_panel:
h = self.get_int_property(context, "horizontal_count")
v = self.get_int_property(context, "vertical_count")
h = max(1, min(8, h))
v = max(1, min(8, v))
display_panels = workspace_controller.apply_layout(display_panel, h, v)
display_panels = workspace_controller.apply_layouts(selected_display_panel, display_panels, h, v)
action_result = Window.ActionResult(Window.ActionStatus.FINISHED)
action_result.results["display_panels"] = list(display_panels)
return action_result
Expand All @@ -3552,6 +3518,30 @@ def is_enabled(self, context: Window.ActionContext) -> bool:
return context.display_panel is not None


class WorkspaceSplitHorizontalAction(WorkspaceSplitAction):
action_id = "workspace.split_horizontal"
action_name = _("Split Panel Into Left and Right")
action_command_icon_png = pkgutil.get_data(__name__, "resources/workspace_split_vertical.png")

def execute(self, context: Window.ActionContext) -> Window.ActionResult:
context = typing.cast(DocumentController.ActionContext, context)
self.set_int_property(context, "horizontal_count", 2)
self.set_int_property(context, "vertical_count", 1)
return super().execute(context)


class WorkspaceSplitVerticalAction(WorkspaceSplitAction):
action_id = "workspace.split_vertical"
action_name = _("Split Panel Into Top and Bottom")
action_command_icon_png = pkgutil.get_data(__name__, "resources/workspace_split_horizontal.png")

def execute(self, context: Window.ActionContext) -> Window.ActionResult:
context = typing.cast(DocumentController.ActionContext, context)
self.set_int_property(context, "horizontal_count", 1)
self.set_int_property(context, "vertical_count", 2)
return super().execute(context)


class WorkspaceSplit2x2Action(WorkspaceSplitAction):
action_id = "workspace.split_2x2"
action_name = _("Split Panel 2x2")
Expand Down
78 changes: 41 additions & 37 deletions nion/swift/Workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -994,50 +994,54 @@ def _remove_display_panel(self, display_panel: DisplayPanel.DisplayPanel,
self.__sync_layout()
return old_display_panel, old_splits, region_id

def apply_layout(self, display_panel: DisplayPanel.DisplayPanel, w: int, h: int) -> typing.List[DisplayPanel.DisplayPanel]:
display_panel_container = display_panel.container
assert display_panel_container
def apply_layouts(self, primary_display_panel: DisplayPanel.DisplayPanel, display_panels: typing.Sequence[DisplayPanel.DisplayPanel], w: int, h: int) -> typing.List[DisplayPanel.DisplayPanel]:
assert self.__workspace
change_workspace_contents_command = ChangeWorkspaceContentsCommand(self, _("Change Workspace Contents"))

new_display_panels = list()

# insert the rows
row_splitter_canvas_item = CanvasItem.SplitterCanvasItem(orientation="horizontal")
row_splitter_canvas_item.on_splits_will_change = functools.partial(self._splits_will_change, row_splitter_canvas_item)
row_splitter_canvas_item.on_splits_changed = functools.partial(self._splits_did_change, row_splitter_canvas_item)
display_panel_container.wrap_canvas_item(display_panel, row_splitter_canvas_item)
new_display_panels.append(display_panel)
dest_index = self.__display_panels.index(display_panel)
for row in range(h):
if row > 0:
row_display_panel = DisplayPanel.DisplayPanel(self.document_controller, dict())
self.__insert_display_panel(dest_index + 1, row_display_panel)
new_display_panels.append(row_display_panel)
dest_index += 1
row_splitter_canvas_item.insert_canvas_item(row + 1, row_display_panel)
else:
row_display_panel = display_panel

# insert the columns
column_splitter_canvas_item = CanvasItem.SplitterCanvasItem(orientation="vertical")
column_splitter_canvas_item.on_splits_will_change = functools.partial(self._splits_will_change, column_splitter_canvas_item)
column_splitter_canvas_item.on_splits_changed = functools.partial(self._splits_did_change, column_splitter_canvas_item)
row_display_panel_container = row_display_panel.container
assert row_display_panel_container
row_display_panel_container.wrap_canvas_item(row_display_panel, column_splitter_canvas_item)
for column in range(1, w):
column_display_panel = DisplayPanel.DisplayPanel(self.document_controller, dict())
new_display_panels.append(column_display_panel)
self.__insert_display_panel(dest_index + 1, column_display_panel)
dest_index += 1
column_splitter_canvas_item.insert_canvas_item(column + 1, column_display_panel)
column_splitter_canvas_item.splits = [1//w] * w

row_splitter_canvas_item.splits = [1//h] * h
for display_panel in display_panels:
# find the display panel container
display_panel_container = display_panel.container
assert display_panel_container

# insert the rows
row_splitter_canvas_item = CanvasItem.SplitterCanvasItem(orientation="horizontal")
row_splitter_canvas_item.on_splits_will_change = functools.partial(self._splits_will_change, row_splitter_canvas_item)
row_splitter_canvas_item.on_splits_changed = functools.partial(self._splits_did_change, row_splitter_canvas_item)
display_panel_container.wrap_canvas_item(display_panel, row_splitter_canvas_item)
new_display_panels.append(display_panel)
dest_index = self.__display_panels.index(display_panel)
for row in range(h):
if row > 0:
row_display_panel = DisplayPanel.DisplayPanel(self.document_controller, dict())
self.__insert_display_panel(dest_index + 1, row_display_panel)
new_display_panels.append(row_display_panel)
dest_index += 1
row_splitter_canvas_item.insert_canvas_item(row + 1, row_display_panel)
else:
row_display_panel = display_panel

# insert the columns
column_splitter_canvas_item = CanvasItem.SplitterCanvasItem(orientation="vertical")
column_splitter_canvas_item.on_splits_will_change = functools.partial(self._splits_will_change, column_splitter_canvas_item)
column_splitter_canvas_item.on_splits_changed = functools.partial(self._splits_did_change, column_splitter_canvas_item)
row_display_panel_container = row_display_panel.container
assert row_display_panel_container
row_display_panel_container.wrap_canvas_item(row_display_panel, column_splitter_canvas_item)
for column in range(1, w):
column_display_panel = DisplayPanel.DisplayPanel(self.document_controller, dict())
new_display_panels.append(column_display_panel)
self.__insert_display_panel(dest_index + 1, column_display_panel)
dest_index += 1
column_splitter_canvas_item.insert_canvas_item(column + 1, column_display_panel)
column_splitter_canvas_item.splits = [1//w] * w

row_splitter_canvas_item.splits = [1//h] * h

self.__sync_layout()
display_panel.request_focus()

primary_display_panel.request_focus()

self.document_controller.push_undo_command(change_workspace_contents_command)

Expand Down
4 changes: 4 additions & 0 deletions nion/swift/resources/changes.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"issues": ["https://github.com/nion-software/nionswift/issues/1002"],
"summary": "Fix issue when removing workspace to select next most recent workspace."
},
{
"issues": ["https://github.com/nion-software/nionswift/issues/1001"],
"summary": "Fix issue when splitting display panels with multiple panels selected."
},
{
"issues": ["https://github.com/nion-software/nionswift/issues/922", "https://github.com/nion-software/nionswift/issues/327"],
"summary": "Make filtering work on modified date text and session metadata text when filtering data items in data panel."
Expand Down
13 changes: 13 additions & 0 deletions nion/swift/test/Workspace_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,19 @@ def test_workspace_remove_bottom_two_in_2x2_undo_and_redo_works_cleanly(self):
# compare against new layout
self.assertEqual(new_workspace_layout, workspace_controller._workspace_layout)

def test_workspace_split_multiple_panels(self):
with TestContext.create_memory_context() as test_context:
document_controller = test_context.create_document_controller()
workspace_controller = document_controller.workspace_controller
self.assertEqual(1, len(workspace_controller.display_panels))
document_controller.selected_display_panel = workspace_controller.display_panels[0]
document_controller.perform_action("workspace.split_2x2")
self.assertEqual(4, len(workspace_controller.display_panels))
document_controller.selected_display_panel = workspace_controller.display_panels[0]
document_controller.add_secondary_display_panel(workspace_controller.display_panels[1])
document_controller.perform_action("workspace.split_vertical")
self.assertEqual(6, len(workspace_controller.display_panels))

def test_workspace_change_workspace_to_data_thumbnail_grid_works(self):
with TestContext.create_memory_context() as test_context:
document_controller = test_context.create_document_controller()
Expand Down

0 comments on commit 7460af8

Please sign in to comment.