forked from wxGlade/wxGlade
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tree.py
635 lines (583 loc) · 27 KB
/
tree.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
"""\
Classes to handle and display the structure of a wxGlade app
@copyright: 2002-2007 Alberto Griggio
@copyright: 2014-2016 Carsten Grohmann
@copyright: 2016-2020 Dietmar Schwertberger
@license: MIT (see LICENSE.txt) - THIS PROGRAM COMES WITH NO WARRANTY
"""
import os.path
import wx
import misc, common, compat, config, clipboard
DEBUG = config.debugging and False
if DEBUG:
import utilities
class WidgetTree(wx.TreeCtrl):#, Tree):
"Tree with the ability to display the hierarchy of widgets"
images = {} # Dictionary of icons of the widgets displayed
def __init__(self, parent, application):
style = wx.TR_DEFAULT_STYLE|wx.TR_HAS_VARIABLE_ROW_HEIGHT
style |= wx.TR_EDIT_LABELS
if wx.Platform == '__WXGTK__': style |= wx.TR_NO_LINES|wx.TR_FULL_ROW_HIGHLIGHT
elif wx.Platform == '__WXMAC__': style &= ~wx.TR_ROW_LINES
wx.TreeCtrl.__init__(self, parent, -1, style=style)
self.cur_widget = None # reference to the selected widget
self.root = application
self._load_images()
application.item = self.AddRoot(_('Application'), 0)
self._SetItemData(application.item, application)
self.skip_select = 0 # avoid an infinite loop on win32, as SelectItem fires an EVT_TREE_SEL_CHANGED event
self.drop_target = clipboard.DropTarget(self, toplevel=True)
self.SetDropTarget(self.drop_target)
self._drag_ongoing = False
self.auto_expand = True # this control the automatic expansion of nodes: it is set to False during xml loading
self.Bind(wx.EVT_TREE_SEL_CHANGED, self.on_change_selection)
self.Bind(wx.EVT_RIGHT_DOWN, self.popup_menu)
self.Bind(wx.EVT_LEFT_DCLICK, self.on_left_dclick)
self.Bind(wx.EVT_LEFT_DOWN, self.on_left_click) # allow direct placement of widgets
self.Bind(wx.EVT_MENU, self.on_menu) # for handling the selection of the first item
self._popup_menu_widget = None # the widget for the popup menu
self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.begin_drag)
self.Bind(wx.EVT_LEAVE_WINDOW, self.on_leave_window)
self.Bind(wx.EVT_MOUSE_EVENTS, self.on_mouse_events)
self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.begin_edit_label)
self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.end_edit_label)
#self.Bind(wx.EVT_KEY_DOWN, misc.on_key_down_event)
self.Bind(wx.EVT_KEY_DOWN, self.on_key_down_event)
#self.Bind(wx.EVT_CHAR_HOOK, self.on_char) # on wx 2.8 the event will not be delivered to the child
self.Bind(wx.EVT_TREE_DELETE_ITEM, self.on_delete_item)
def _load_images(self):
image_list = wx.ImageList(21, 21)
image_list.Add(wx.Bitmap(os.path.join(config.icons_path, 'application.xpm'), wx.BITMAP_TYPE_XPM))
bitmaps = {}
for name, path in WidgetTree.images.items():
bitmaps[name] = misc.get_xpm_bitmap(path)
for name in ["EditSizerSlot"]:
bmp = bitmaps[name]
if hasattr(bitmaps[name], "ConvertToDisabled"):
bmp = bmp.ConvertToDisabled()
bitmaps[name+"-Disabled"] = bmp
for name, bitmap in bitmaps.items():
WidgetTree.images[name] = image_list.Add(bitmap)
self.AssignImageList(image_list)
def on_char(self, event):
"called from main: start label editing on F2; skip events while editing"
keycode = event.GetKeyCode()
if keycode==wx.WXK_F2 and self.cur_widget and self.cur_widget._label_editable():
# start label editing
self.EditLabel( self.cur_widget.item )
return True
if isinstance(self.FindFocus(), wx.TextCtrl):
# currently editing
event.Skip()
return True
if keycode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT):
event.Skip()
return True
if keycode==wx.WXK_RETURN and self.cur_widget and self.cur_widget.item:
if self.cur_widget.IS_SLOT:
if self.cur_widget.overlapped: return True
return False
if self.cur_widget.IS_TOPLEVEL and self.cur_widget.children:
self.show_toplevel(None, editor=self.cur_widget)
return True
elif not self.IsExpanded(self.cur_widget.item) and self.cur_widget.children:
self.ExpandAllChildren(self.cur_widget.item)
return True
elif self.IsExpanded(self.cur_widget.item):
self.Collapse(self.cur_widget.item)
return True
return False
def on_key_down_event(self, event):
#if event.GetKeyCode()==13:
## go to property editor
#common.property_panel.SetFocus()
#return
if event.GetKeyCode()==wx.WXK_WINDOWS_MENU and self.cur_widget:
# windows menu key pressed -> display context menu for selected item
item = self.RootItem if self.cur_widget is common.root else self.cur_widget.item
if item:
rect = self.GetBoundingRect(item, textOnly=True)
if rect is not None:
pos = (rect.right, (rect.top+rect.bottom)//2)
self.cur_widget.popup_menu(event, pos=pos)
return
#misc.on_key_down_event(event)
event.Skip()
def begin_drag(self, evt):
# start drag & drop
item = evt.GetItem()
widget = self._GetItemData(item)
if not widget:
if config.debugging: raise ValueError("internal error")
return
if widget is self.root or widget.IS_SLOT: return # application and slots can't be dragged
self._drag_ongoing = True
clipboard.begin_drag(self, widget)
self._drag_ongoing = False
def begin_edit_label(self, evt):
# Begin editing a label. This can be prevented by calling Veto()
widget = self._GetItemData( evt.Item )
if not widget or not widget._label_editable(): evt.Veto()
def _split_name_label(self, new_value):
# split user input into name and label; if there's no colon but a quotation mark, it's just a label
new_name = new_label = None
new_value = new_value.strip()
if new_value.endswith("'") and ": '" in new_value:
new_name, new_label = new_value.split(": '", 1)
new_label = new_label[:-1]
elif new_value.endswith('"') and ': "' in new_value:
new_name, new_label = new_value.split(': "', 1)
new_label = new_label[:-1]
elif new_value and (new_value[0] in ["'",'"'] or new_value[-1] in ["'",'"']):
# just a label; for this we accept errors, we just want at least one quotation mark
if new_value[0]==new_value[-1] and len(new_value)>2:
new_label = new_value[1:-1]
elif new_value[0] in ["'",'"']:
new_label = new_value[1:]
elif new_value[-1] in ["'",'"']:
new_label = new_value[:-1]
elif not ":" in new_value:
new_name = new_value
return new_name, new_label
def end_edit_label(self, evt):
# Finish editing a label. This can be prevented by calling Veto()
if evt.IsEditCancelled(): return
item = evt.Item
widget = self._GetItemData( item )
if "name" not in widget.properties: return
new_value = evt.Label
if new_value==widget._get_tree_label(): return
new_name = new_label = new_title = new_tab = new_class = new_stockitem = None
if widget.check_prop_truth("class"):
if new_value.count("(")==1 and new_value.count(")")==1:
pre, new_class = new_value.split("(")
new_class, post = new_class.split(")")
if pre.endswith(" "): pre = pre[:-1]
new_value = pre+post
if widget.check_prop_truth("stockitem"):
if ":" in new_value:
new_name, new_stockitem = new_value.split(":", 1)
new_stockitem = new_stockitem.strip().upper()
if not new_stockitem in widget.STOCKITEMS: new_stockitem = None
elif new_value.strip().upper() in widget.STOCKITEMS:
new_stockitem = new_value.strip().upper()
else:
new_name = new_value
elif "label" in widget.properties and widget._label_editable():
new_name, new_label = self._split_name_label(new_value)
elif "label" in widget.properties:
# label is not editable, but name may have changed
new_name, dummy = self._split_name_label(new_value)
elif getattr(widget, "has_title", None):
new_name, new_title = self._split_name_label(new_value)
elif getattr(widget, "parent", None) and widget.parent.WX_CLASS=="wxNotebook" and "]" in new_value:
# notebook pages: include page title: "[title] name"
new_tab, new_name = new_value.rsplit("]",1)
if "[" in new_tab: new_tab = new_tab.split("[",1)[-1]
new_name = new_name.strip()
else:
new_name = new_value
# check name
if new_name:
name_p = widget.properties["name"]
if new_name==name_p.get(): new_name = None
if new_name:
# check
warning, error = name_p.check_value(new_name)
if warning or error: new_name = None
# check class/klass
if new_class:
class_p = widget.properties["klass"]
if new_class==class_p.get(): new_class = None
if new_class:
# check
warning, error = name_p.check_value(new_class)
if warning or error: new_class = None
# check label
if new_label is not None:
label_p = widget.properties["label"]
if new_label==label_p.get(): new_label = None
if (not new_name and new_label is None and new_title is None and new_tab is None and new_class is None
and not new_stockitem):
# no change or an error
wx.Bell()
evt.Veto()
return
# check title
if new_stockitem is not None:
stockitem_p = widget.properties["stockitem"]
if new_stockitem==stockitem_p.get(): new_stockitem = None
# check title
if new_title is not None:
title_p = widget.properties["title"]
if new_title==title_p.get(): new_title = None
# check notabook tab
if new_tab is not None:
notebook = widget.parent
tabs_p = notebook.properties["tabs"]
if widget in notebook.children:
index = notebook.children.index(widget)
if notebook.tabs[index][0]==new_tab:
new_tab = None
else:
new_tabs = notebook.tabs[:]
new_tabs[index][0] = new_tab
else:
new_tab = None
# actually modify the values
modified = set()
if new_name:
name_p.previous_value = name_p.value
name_p.set(new_name, notify=False)
modified.add("name")
if new_class:
class_p.previous_value = class_p.value
class_p.set(new_class, notify=False)
modified.add("class")
if new_label:
label_p.previous_value = label_p.value
label_p.set(new_label, notify=False)
modified.add("label")
if new_stockitem:
stockitem_p.previous_value = stockitem_p.value
stockitem_p.set(new_stockitem, notify=False)
modified.add("stockitem")
if new_title:
title_p.previous_value = title_p.value
title_p.set(new_title, notify=False)
modified.add("title")
if modified:
widget.properties_changed(modified)
self.root.saved = False # update the status of the app
if new_tab:
tabs_p.previous_value = tabs_p.value
tabs_p.set(new_tabs, notify=True)
wx.CallAfter( self.refresh, widget, refresh_label=True) # setting from within the event handler does not work
if compat.IS_CLASSIC:
def _SetItemData(self, item, data):
self.SetPyData(item, data)
def _GetItemData(self, item):
if not bool(item): return None
return self.GetPyData(item)
else:
def _SetItemData(self, item, data):
self.SetItemData(item, data)
def _GetItemData(self, item):
if not bool(item): return None
try:
return self.GetItemData(item)
except RuntimeError:
# on GTK the above bool may return True even if the item is being deleted
return None
def add2(self, child, parent, index, item=None):
"insert an item for child into the list of parent's items; optionally re-use old item"
image = self.images.get( child._get_tree_image(), -1)
label = child._get_tree_label()
if item is None:
if index is None:
item = child.item = self.AppendItem(parent.item, label, image)
else:
item = child.item = compat.wx_Tree_InsertItemBefore(self, parent.item, index, label, image)
else:
# re-use
child.item = item
self.refresh(child)
self._SetItemData(item, child)
if self.auto_expand:
self.Expand(parent.item)
if DEBUG:
print("added item", utilities.hx(item), child, child.item)
return item
def remove(self, editor):
# just remove the mutual references between editor and tree item
if editor.item is None: return # could be during common.root.clear() when there is an error on loading
self._SetItemData(editor.item, None)
editor.item = None
####################################################################################################################
# new implementation:
def on_delete_item(self, event):
item = event.GetItem()
editor = self._GetItemData( item )
if DEBUG:
print("on_delete_item", utilities.hx(item), editor, editor and editor.item or None)
if editor is not None and editor.item is item:
editor.item = None
def _get_children_items(self, item):
items = []
child_item, cookie = self.GetFirstChild(item)
while child_item.IsOk():
items.append(child_item)
child_item, cookie = self.GetNextChild(item, cookie)
return items
def _build_children(self, editor, item, recursive=True):
# XXX a better algorithm would be nice
# currently it's checked from the start and from the end how many are matching; all inbetween are replaced
if DEBUG: print("_build_children", editor)
children = editor.get_all_children()
items = self._get_children_items(editor.item)
if DEBUG: print("children", children)
if DEBUG: print("items", items)
item_editors = []
for child_item in items:
child = self._GetItemData(child_item)
if child is not None and (not children or not child in children):
self._SetItemData(child_item, None)
if child.item is child_item:
if DEBUG: print("removed child.item", utilities.hx(child), utilities.hx(child.item))
child.item = None # is probably None already
item_editors.append(None)
else:
item_editors.append(child)
if DEBUG: print("item_editors", item_editors)
if DEBUG: print()
match_beginning = 0
for c,child in enumerate(children):
if c<len(item_editors) and item_editors[c] is child:
match_beginning = c+1
else:
break
match_end = 0
c = -1
while match_beginning+match_end<len(children):
if match_end<len(item_editors) and item_editors[c] is children[c]:
match_end += 1
else:
break
c -= 1
if len(children) > len(item_editors):
# insert or add items, right after match_beginning
for n in range( len(children) - len(item_editors) ):
index = match_beginning + n
child = children[index]
item = self.add2(child, parent=editor, index=index)
items.insert(index, item)
elif len(children) < len(item_editors):
# remove items, right after match_beginning
for n in range( len(item_editors) - len(children) ):
index = match_beginning
child_item = items[index]
self.Delete(child_item)
del items[match_beginning]
if match_beginning+match_end<len(children):
# length matches, re-use item in the middle
for index in range(match_beginning, len(children)-match_end):
child = children[index]
item = self.add2(child, parent=editor, index=index, item=items[index])
if not recursive:
# update labels and images, called e.g. when notebook pages change
for child in children:
self.refresh(child)
return
for child, item in zip(children, items):
self._build_children(child, item)
def build(self, editor=None, recursive=True, freeze=False):
if DEBUG:
print("="*80)
print("build", editor, recursive)
# (re-)build tree from data structure
# e.g. .build(control)
# XXX if recursive is not True, ensure that all children are refreshed, as e.g. slot numbers may have changed
if freeze: self.Freeze() # Freeze/Thaw makes the tree scroll around, so it should only be used for major change
try:
if editor is None:
editor = self.root
item = self.GetRootItem()
else:
item = editor.item
if item is None and editor.parent.item:
# check whether at the same position there is an item already without an editor
pos = editor.parent._get_child_pos(editor)
items = self._get_children_items(editor.parent.item)
if len(items)>pos and self._GetItemData(items[pos]) is None:
item = items[pos]
editor.item = item
self._SetItemData(item, editor)
self.refresh(editor)
while item is None:
editor = editor.parent
item = editor.item
self._build_children(editor, item, recursive)
finally:
if freeze: self.Thaw()
#if config.debugging or DEBUG:
if DEBUG:
import utilities
utilities.TreePrinter(editor)
def OnCompareItems(self, item1, item2):
# only used when re-ordering toplevel items
editor1 = self._GetItemData(item1)
editor2 = self._GetItemData(item2)
parent = editor1.parent
assert parent is editor2.parent and parent is common.root
index1 = parent.children.index(editor1)
index2 = parent.children.index(editor2)
if index1<index2: return -1
return +1
def refresh(self, editor, refresh_label=True, refresh_image=True):
# refresh label and/or image
if editor.item is None: return
if refresh_label:
self.SetItemText(editor.item, editor._get_tree_label())
if refresh_image:
image = self.images.get( editor._get_tree_image(), -1)
self.SetItemImage(editor.item, image)
def select_item(self, editor):
self.skip_select = True
self.SelectItem(editor.item)
self.skip_select = False
self._set_cur_widget(editor)
misc.set_focused_widget(self.cur_widget)
def _set_cur_widget(self, editor):
# set self.cur_widget; adjust label colors and bold if required (on Windows)
if self.cur_widget and wx.Platform == "__WXMSW__" and self.cur_widget.item:
item = self.cur_widget.item
self.SetItemTextColour(item, wx.NullColour)
self.SetItemBold( item, False )
self.cur_widget = editor
item = editor.item
self.EnsureVisible(item)
# ensure that the icon is visible
text_rect = self.GetBoundingRect(item, True)
if text_rect.x<22:
self.SetScrollPos(wx.HORIZONTAL, self.GetScrollPos(wx.HORIZONTAL) - 22 + text_rect.x)
if wx.Platform == "__WXMSW__":
self.SetItemBold(item, True)
self.SetItemTextColour(item, wx.BLUE)
s = editor._get_tooltip_string()
common.main.user_message( s and s.replace("\n", " ") or "" )
def set_current_widget(self, editor):
# interface from common.set_focused_widget
if editor is None or editor is self.cur_widget or editor.item is None: return
self.skip_select = True
self.SelectItem(editor.item)
if not self.IsExpanded(editor.item) and (not hasattr(self, "HasFocus") or not self.HasFocus()):
self.Expand(editor.item)
self.skip_select = False
self._set_cur_widget(editor)
def on_change_selection(self, event):
if self.skip_select:
event.Skip()
return # triggered by self.SelectItem in self.set_current_widget
item = event.GetItem()
editor = self._GetItemData(item)
if not editor: # can happen during build/rebuild_tree
event.Skip()
return
self._set_cur_widget(editor)
misc.set_focused_widget(editor)
if not self.IsExpanded(item) and (not hasattr(self, "HasFocus") or not self.HasFocus()):
self.Expand(item)
self.SetFocus()
def on_left_click(self, event):
if not common.adding_widget: return event.Skip()
editor = self.find_editor_by_pos( *event.GetPosition() )
if not editor: return event.Skip()
compatible, message = editor.check_drop_compatibility()
if not compatible:
event.Skip()
misc.error_message(message)
return
common.adding_window = event.GetEventObject().GetTopLevelParent()
if editor.IS_SLOT:
editor.on_drop_widget(event)
elif common.adding_sizer or True:
editor.drop_sizer()
common.adding_window = None
def on_left_dclick(self, event):
x, y = event.GetPosition()
editor = self.find_editor_by_pos(x, y)
if not editor:
event.Skip()
return
if editor.WX_CLASS=='wxMenuBar':
editor.properties["menus"].edit_menus()
elif editor.WX_CLASS=='wxToolBar':
editor.properties["tools"].edit_tools()
elif editor.IS_TOPLEVEL:
self.show_toplevel(None, editor)
else:
event.Skip()
def on_leave_window(self, event):
self.SetCursor(wx.STANDARD_CURSOR)
event.Skip()
def on_menu(self, event):
# the first entry in the popup menu, i.e. the name was selected
if self._popup_menu_widget is None: return
if self._popup_menu_widget.IS_TOPLEVEL_WINDOW:
self.show_toplevel( None, self._popup_menu_widget )
def on_mouse_events(self, event):
if not self._drag_ongoing and not event.IsButton():
message = None
# set cursor to indicate a possible drop
x,y = event.GetPosition()
editor = self.find_editor_by_pos(x, y, toplevels_only=False)
if editor is not None:
if not common.adding_widget:
self.SetCursor(wx.STANDARD_CURSOR)
else:
# check whether the item can be dropped here
compatible, message = editor.check_drop_compatibility()
if compatible:
self.SetCursor(wx.CROSS_CURSOR) # a Cursor instance
else:
compat.SetCursor(self, wx.CURSOR_NO_ENTRY)
else:
self.SetCursor(wx.STANDARD_CURSOR)
compat.SetToolTip(self, message or "")
event.Skip()
def popup_menu(self, event, pos=None):
editor = self.find_editor_by_pos(*event.GetPosition())
if not editor: return
self.select_item(editor)
self._popup_menu_widget = editor
editor.popup_menu(event, pos)
self._popup_menu_widget = None
def show_toplevel(self, event, editor=None):
"Event handler for left double-clicks: if the click is above a toplevel widget and this is hidden, shows it"
if editor is None:
try: x, y = event.GetPosition()
except AttributeError:
# if we are here, event is a CommandEvent and not a MouseEvent
editor = self._GetItemData(self.GetSelection())
self.ExpandAllChildren(editor.item) # if we are here, the widget must be shown
else:
editor = self.find_editor_by_pos(x, y, toplevels_only=True)
if editor is None or editor.IS_ROOT: return
# the actual toplevel widget may be one level higher, e.g. for a Panel, which is embedded in a Frame
set_size = None
if editor.IS_TOPLEVEL:
# toplevel window or a menu/status bar
toplevel_widget = editor.widget
if editor.check_prop("size") and editor.check_prop("toolbar") and editor.toolbar:
# apply workaround for size changes due to a toolbar; this would cause problems with automatic testing
set_size = editor.properties.get("size").get_size()
else:
toplevel_widget = editor.widget.GetParent()
if not editor.is_visible():
# added by rlawson to expand node on showing top level widget
self.ExpandAllChildren(editor.item)
editor.show_widget()
if wx.Platform != '__WXMSW__' and set_size is not None:
# XXX integrate with above or remove above again?
toplevel_widget = editor.widget # above it was not yet created
wx.CallAfter(toplevel_widget.SetSize, set_size)
else:
editor.hide_widget()
#toplevel_widget.GetTopLevelParent().Hide()
if event: event.Skip()
if "design" in editor.properties: editor.design.update_label()
def find_editor_by_pos(self, x, y, toplevels_only=False):
"""Finds the node which is displayed at the given coordinates. Returns None if there's no match.
If toplevels_only is True, scans only root's children"""
item, flags = self.HitTest((x, y))
if item and flags & (wx.TREE_HITTEST_ONITEMLABEL | wx.TREE_HITTEST_ONITEMICON):
editor = self._GetItemData(item)
if not toplevels_only or editor.parent is self.root:
return editor
return None
def change_item_editor(self, old, new, keep_children=False):
# called from edit_sizers.change_sizer
self._SetItemData(old.item, new)
new.item = old.item
old.item = None
self.refresh(new)