Skip to content

Commit

Permalink
Implement drag select for widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
ObaraEmmanuel committed Sep 29, 2023
1 parent d2e481c commit c4daa23
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 2 deletions.
19 changes: 19 additions & 0 deletions studio/feature/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from studio.parsers.loader import DesignBuilder, BaseStudioAdapter
from studio.ui import geometry
from studio.ui.widgets import DesignPad, CoordinateIndicator
from studio.ui.highlight import RegionHighlighter
from studio.context import BaseContext
from studio import __version__

Expand Down Expand Up @@ -132,6 +133,7 @@ def __init__(self, master, studio):
self._shortcut_mgr = KeyMap(self._frame)
self._set_shortcuts()
self._last_click_pos = None
self._region_highlight = RegionHighlighter(self, self.style)

self._empty = Label(
self,
Expand Down Expand Up @@ -525,6 +527,23 @@ def layout_at(self, bounds):

return candidate or self

def show_select_region(self, bounds):
bounds = geometry.resolve_bounds(bounds, self)
self._region_highlight.highlight_bounds(bounds)

def clear_select_region(self):
self._region_highlight.clear()

def select_in_region(self, widget, bounds):
if isinstance(widget, Container):
bounds = geometry.resolve_bounds(bounds, self)
to_select = []
for child in widget._children:
if geometry.compute_overlap(child.get_bounds(), bounds):
to_select.append(child)
if to_select:
self.studio.selection.set(to_select)

def _attach(self, obj):
# bind events for context menu and object selection
# all widget additions call this method so clear empty message
Expand Down
1 change: 1 addition & 0 deletions studio/lib/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Canvas(PseudoWidget, tk.Canvas):
icon = "paint"
impl = tk.Canvas
allow_direct_move = False
allow_drag_select = False

def __init__(self, master, id_):
super().__init__(master)
Expand Down
26 changes: 24 additions & 2 deletions studio/lib/pseudo.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class PseudoWidget:
impl = None
is_toplevel = False
allow_direct_move = True
allow_drag_select = False
# special handlers (intercepts) for attributes that need additional processing
# to interface with the studio easily
_intercepts = {
Expand Down Expand Up @@ -145,6 +146,8 @@ def setup_widget(self):
self.bind("<Motion>", self._on_drag, add='+')

self._active = False
self._select_mode_active = False
self._select_bounds = None
self.prev_stack_index = None

def set_name(self, name):
Expand Down Expand Up @@ -173,23 +176,41 @@ def lift(self, above_this):
super().lift(above_this)

def _on_press(self, event):
if not self.allow_direct_move and not event.state & EventMask.SHIFT:
if not self.allow_drag_select:
return
self._select_mode_active = True
self._pos_fix = (event.x_root, event.y_root)
return
if not self._handle:
return
self._pos_fix = (event.x_root, event.y_root)
self.handle_active('all')
self._active = True

def _on_release(self, _):
if self._select_mode_active:
self.designer.clear_select_region()
self._select_mode_active = False
if self._select_bounds is not None:
self.designer.select_in_region(self, self._select_bounds)
self._select_bounds = None

if not self._active:
return
self.handle_inactive('all')
self._active = False

def _on_drag(self, event):
if self._select_mode_active:
x1, y1 = self._pos_fix
x2, y2 = event.x_root, event.y_root
bounds = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)
self._select_bounds = bounds
self.designer.show_select_region(bounds)

if not self._active or not event.state & EventMask.MOUSE_BUTTON_1:
return
if not self.allow_direct_move and not event.state & EventMask.SHIFT:
return
x, y = self._pos_fix
self._pos_fix = (event.x_root, event.y_root)
self.handle_resize('all', (event.x_root - x, event.y_root - y))
Expand Down Expand Up @@ -379,6 +400,7 @@ def __repr__(self):
class Container(PseudoWidget):
LAYOUTS = layouts.layouts
allow_direct_move = False
allow_drag_select = True

def setup_widget(self):
self.parent = self.designer = self._get_designer()
Expand Down
4 changes: 4 additions & 0 deletions studio/ui/highlight.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ def clear(self):
element.place_forget()


class RegionHighlighter(WidgetHighlighter):
OUTLINE = 1


class EdgeIndicator(tk.Frame):
"""
Generates a conspicuous line at the edges of a widget for various indication purposes
Expand Down

0 comments on commit c4daa23

Please sign in to comment.