meerk40t 0.9.2000__py2.py3-none-any.whl → 0.9.3001__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. meerk40t/balormk/balor_params.py +1 -43
  2. meerk40t/balormk/controller.py +1 -41
  3. meerk40t/balormk/device.py +16 -22
  4. meerk40t/balormk/driver.py +4 -4
  5. meerk40t/balormk/gui/balorconfig.py +2 -2
  6. meerk40t/balormk/gui/balorcontroller.py +13 -5
  7. meerk40t/balormk/gui/baloroperationproperties.py +0 -46
  8. meerk40t/balormk/gui/gui.py +17 -17
  9. meerk40t/camera/gui/camerapanel.py +18 -11
  10. meerk40t/core/cutcode/rastercut.py +3 -1
  11. meerk40t/core/cutplan.py +145 -14
  12. meerk40t/core/elements/clipboard.py +18 -9
  13. meerk40t/core/elements/element_treeops.py +320 -180
  14. meerk40t/core/elements/element_types.py +7 -2
  15. meerk40t/core/elements/elements.py +53 -27
  16. meerk40t/core/elements/geometry.py +8 -0
  17. meerk40t/core/elements/offset_clpr.py +129 -4
  18. meerk40t/core/elements/offset_mk.py +3 -1
  19. meerk40t/core/elements/shapes.py +28 -25
  20. meerk40t/core/laserjob.py +7 -0
  21. meerk40t/core/node/bootstrap.py +4 -0
  22. meerk40t/core/node/effect_hatch.py +85 -96
  23. meerk40t/core/node/effect_wobble.py +309 -0
  24. meerk40t/core/node/elem_image.py +49 -19
  25. meerk40t/core/node/elem_line.py +60 -0
  26. meerk40t/core/node/elem_rect.py +5 -3
  27. meerk40t/core/node/image_processed.py +766 -0
  28. meerk40t/core/node/image_raster.py +113 -0
  29. meerk40t/core/node/node.py +120 -1
  30. meerk40t/core/node/op_cut.py +2 -8
  31. meerk40t/core/node/op_dots.py +0 -8
  32. meerk40t/core/node/op_engrave.py +2 -18
  33. meerk40t/core/node/op_image.py +22 -35
  34. meerk40t/core/node/op_raster.py +0 -9
  35. meerk40t/core/planner.py +32 -2
  36. meerk40t/core/svg_io.py +699 -461
  37. meerk40t/core/treeop.py +191 -0
  38. meerk40t/core/undos.py +15 -1
  39. meerk40t/core/units.py +14 -4
  40. meerk40t/device/dummydevice.py +3 -2
  41. meerk40t/device/gui/defaultactions.py +43 -55
  42. meerk40t/device/gui/formatterpanel.py +58 -49
  43. meerk40t/device/gui/warningpanel.py +12 -12
  44. meerk40t/device/mixins.py +13 -0
  45. meerk40t/dxf/dxf_io.py +9 -5
  46. meerk40t/extra/ezd.py +28 -26
  47. meerk40t/extra/imageactions.py +300 -308
  48. meerk40t/extra/lbrn.py +19 -2
  49. meerk40t/fill/fills.py +6 -6
  50. meerk40t/fill/patternfill.py +1061 -1061
  51. meerk40t/fill/patterns.py +2 -6
  52. meerk40t/grbl/controller.py +168 -52
  53. meerk40t/grbl/device.py +23 -18
  54. meerk40t/grbl/driver.py +39 -0
  55. meerk40t/grbl/emulator.py +79 -19
  56. meerk40t/grbl/gcodejob.py +10 -0
  57. meerk40t/grbl/gui/grblconfiguration.py +2 -2
  58. meerk40t/grbl/gui/grblcontroller.py +24 -8
  59. meerk40t/grbl/gui/grblhardwareconfig.py +153 -0
  60. meerk40t/grbl/gui/gui.py +17 -14
  61. meerk40t/grbl/mock_connection.py +15 -34
  62. meerk40t/grbl/plugin.py +0 -4
  63. meerk40t/grbl/serial_connection.py +2 -1
  64. meerk40t/gui/about.py +8 -5
  65. meerk40t/gui/alignment.py +10 -6
  66. meerk40t/gui/basicops.py +27 -17
  67. meerk40t/gui/bufferview.py +2 -2
  68. meerk40t/gui/choicepropertypanel.py +101 -13
  69. meerk40t/gui/consolepanel.py +12 -9
  70. meerk40t/gui/devicepanel.py +38 -25
  71. meerk40t/gui/executejob.py +6 -4
  72. meerk40t/gui/help_assets/help_assets.py +13 -10
  73. meerk40t/gui/hersheymanager.py +8 -6
  74. meerk40t/gui/icons.py +1951 -3065
  75. meerk40t/gui/imagesplitter.py +14 -7
  76. meerk40t/gui/keymap.py +3 -3
  77. meerk40t/gui/laserpanel.py +151 -84
  78. meerk40t/gui/laserrender.py +61 -70
  79. meerk40t/gui/lasertoolpanel.py +8 -7
  80. meerk40t/gui/materialtest.py +3 -3
  81. meerk40t/gui/mkdebug.py +254 -1
  82. meerk40t/gui/navigationpanels.py +321 -180
  83. meerk40t/gui/notes.py +3 -3
  84. meerk40t/gui/opassignment.py +12 -12
  85. meerk40t/gui/operation_info.py +13 -13
  86. meerk40t/gui/plugin.py +5 -0
  87. meerk40t/gui/position.py +20 -18
  88. meerk40t/gui/preferences.py +21 -6
  89. meerk40t/gui/propertypanels/attributes.py +70 -22
  90. meerk40t/gui/propertypanels/blobproperty.py +2 -2
  91. meerk40t/gui/propertypanels/consoleproperty.py +2 -2
  92. meerk40t/gui/propertypanels/groupproperties.py +3 -3
  93. meerk40t/gui/propertypanels/hatchproperty.py +11 -18
  94. meerk40t/gui/propertypanels/imageproperty.py +4 -3
  95. meerk40t/gui/propertypanels/opbranchproperties.py +1 -1
  96. meerk40t/gui/propertypanels/pathproperty.py +2 -2
  97. meerk40t/gui/propertypanels/pointproperty.py +2 -2
  98. meerk40t/gui/propertypanels/propertywindow.py +4 -4
  99. meerk40t/gui/propertypanels/textproperty.py +3 -3
  100. meerk40t/gui/propertypanels/wobbleproperty.py +204 -0
  101. meerk40t/gui/ribbon.py +367 -259
  102. meerk40t/gui/scene/scene.py +31 -5
  103. meerk40t/gui/scenewidgets/elementswidget.py +12 -4
  104. meerk40t/gui/scenewidgets/gridwidget.py +2 -2
  105. meerk40t/gui/scenewidgets/laserpathwidget.py +7 -2
  106. meerk40t/gui/scenewidgets/machineoriginwidget.py +6 -2
  107. meerk40t/gui/scenewidgets/relocatewidget.py +1 -1
  108. meerk40t/gui/scenewidgets/reticlewidget.py +9 -0
  109. meerk40t/gui/scenewidgets/selectionwidget.py +12 -7
  110. meerk40t/gui/simpleui.py +95 -8
  111. meerk40t/gui/simulation.py +44 -36
  112. meerk40t/gui/spoolerpanel.py +124 -26
  113. meerk40t/gui/statusbarwidgets/defaultoperations.py +18 -6
  114. meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
  115. meerk40t/gui/statusbarwidgets/opassignwidget.py +12 -12
  116. meerk40t/gui/statusbarwidgets/shapepropwidget.py +45 -18
  117. meerk40t/gui/statusbarwidgets/statusbar.py +11 -4
  118. meerk40t/gui/themes.py +78 -0
  119. meerk40t/gui/toolwidgets/toolcircle.py +2 -1
  120. meerk40t/gui/toolwidgets/toolellipse.py +2 -1
  121. meerk40t/gui/toolwidgets/toolimagecut.py +132 -0
  122. meerk40t/gui/toolwidgets/toolline.py +144 -0
  123. meerk40t/gui/toolwidgets/toolnodeedit.py +72 -145
  124. meerk40t/gui/toolwidgets/toolpoint.py +1 -1
  125. meerk40t/gui/toolwidgets/toolpolygon.py +8 -55
  126. meerk40t/gui/toolwidgets/toolrect.py +2 -1
  127. meerk40t/gui/usbconnect.py +2 -2
  128. meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +2 -2
  129. meerk40t/gui/utilitywidgets/harmonograph.py +7 -7
  130. meerk40t/gui/utilitywidgets/scalewidget.py +1 -1
  131. meerk40t/gui/wordlisteditor.py +33 -18
  132. meerk40t/gui/wxmeerk40t.py +166 -66
  133. meerk40t/gui/wxmmain.py +236 -157
  134. meerk40t/gui/wxmribbon.py +49 -25
  135. meerk40t/gui/wxmscene.py +49 -38
  136. meerk40t/gui/wxmtree.py +216 -85
  137. meerk40t/gui/wxutils.py +62 -4
  138. meerk40t/image/imagetools.py +443 -15
  139. meerk40t/internal_plugins.py +2 -10
  140. meerk40t/kernel/kernel.py +12 -4
  141. meerk40t/lihuiyu/controller.py +7 -7
  142. meerk40t/lihuiyu/device.py +3 -1
  143. meerk40t/lihuiyu/driver.py +3 -0
  144. meerk40t/lihuiyu/gui/gui.py +8 -8
  145. meerk40t/lihuiyu/gui/lhyaccelgui.py +2 -2
  146. meerk40t/lihuiyu/gui/lhycontrollergui.py +73 -27
  147. meerk40t/lihuiyu/gui/lhydrivergui.py +2 -2
  148. meerk40t/lihuiyu/gui/tcpcontroller.py +22 -9
  149. meerk40t/main.py +6 -1
  150. meerk40t/moshi/controller.py +5 -5
  151. meerk40t/moshi/device.py +5 -2
  152. meerk40t/moshi/driver.py +4 -0
  153. meerk40t/moshi/gui/gui.py +8 -8
  154. meerk40t/moshi/gui/moshicontrollergui.py +24 -8
  155. meerk40t/moshi/gui/moshidrivergui.py +2 -2
  156. meerk40t/newly/controller.py +2 -0
  157. meerk40t/newly/device.py +9 -2
  158. meerk40t/newly/driver.py +4 -0
  159. meerk40t/newly/gui/gui.py +16 -17
  160. meerk40t/newly/gui/newlyconfig.py +2 -2
  161. meerk40t/newly/gui/newlycontroller.py +13 -5
  162. meerk40t/rotary/gui/gui.py +2 -2
  163. meerk40t/rotary/gui/rotarysettings.py +2 -2
  164. meerk40t/ruida/device.py +3 -0
  165. meerk40t/ruida/driver.py +4 -0
  166. meerk40t/ruida/gui/gui.py +6 -6
  167. meerk40t/ruida/gui/ruidaconfig.py +2 -2
  168. meerk40t/ruida/gui/ruidacontroller.py +13 -5
  169. meerk40t/svgelements.py +9 -9
  170. meerk40t/tools/geomstr.py +849 -153
  171. meerk40t/tools/kerftest.py +8 -4
  172. meerk40t/tools/livinghinges.py +15 -8
  173. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/METADATA +21 -16
  174. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/RECORD +185 -177
  175. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/entry_points.txt +0 -1
  176. test/test_core_elements.py +8 -24
  177. test/test_file_svg.py +88 -0
  178. test/test_fill.py +9 -9
  179. test/test_geomstr.py +258 -8
  180. test/test_kernel.py +4 -0
  181. test/test_tools_rasterplotter.py +29 -0
  182. meerk40t/extra/embroider.py +0 -56
  183. meerk40t/extra/pathoptimize.py +0 -249
  184. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/LICENSE +0 -0
  185. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/WHEEL +0 -0
  186. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/top_level.txt +0 -0
  187. {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/zip-safe +0 -0
meerk40t/gui/ribbon.py CHANGED
@@ -7,13 +7,13 @@ The primary method of defining a panel is by calling the `set_buttons()` on the
7
7
  control_panel.set_buttons(
8
8
  {
9
9
  "label": _("Red Dot On"),
10
- "icon": icons8_quick_mode_on_50,
10
+ "icon": icons8_flash_on,
11
11
  "tip": _("Turn Redlight On"),
12
12
  "action": lambda v: service("red on\n"),
13
13
  "toggle": {
14
14
  "label": _("Red Dot Off"),
15
15
  "action": lambda v: service("red off\n"),
16
- "icon": icons8_flash_off_50,
16
+ "icon": icons8_flash_off,
17
17
  "signal": "grbl_red_dot",
18
18
  },
19
19
  "rule_enabled": lambda v: has_red_dot_enabled(),
@@ -53,8 +53,9 @@ from meerk40t.svgelements import Color
53
53
 
54
54
  _ = wx.GetTranslation
55
55
 
56
- SMALL_RESIZE_FACTOR = 2 / 3
57
- TINY_RESIZE_FACTOR = 0.5
56
+ COLOR_MODE_DEFAULT = 0
57
+ COLOR_MODE_COLOR = 1
58
+ COLOR_MODE_DARK = 2
58
59
 
59
60
 
60
61
  class DropDown:
@@ -101,20 +102,22 @@ class Button:
101
102
  self._aspects = {}
102
103
  self.key = "original"
103
104
  self.object = None
104
- self.bitmapsize = "large"
105
105
 
106
106
  self.position = None
107
107
  self.toggle = False
108
108
 
109
109
  self.label = None
110
+ self.icon = None
111
+
110
112
  self.bitmap = None
111
113
  self.bitmap_disabled = None
112
- self.bitmap_tiny_disabled = None
113
- self.bitmap_small_disabled = None
114
- self.bitmap_large_disabled = None
115
- self.bitmap_tiny = None
116
- self.bitmap_small = None
117
- self.bitmap_large = None
114
+
115
+ self.min_size = 15
116
+ self.max_size = 150
117
+
118
+ self.available_bitmaps = {}
119
+ self.available_bitmaps_disabled = {}
120
+
118
121
  self.tip = None
119
122
  self.client_data = None
120
123
  self.state = 0
@@ -132,35 +135,11 @@ class Button:
132
135
  self.rule_visible = None
133
136
  self.min_width = 0
134
137
  self.min_height = 0
135
- self.default_width = 50
138
+ self.default_width = int(self.max_size / 2)
139
+ self.icon_size = self.default_width
140
+
136
141
  self.set_aspect(**description)
137
142
  self.apply_enable_rules()
138
- # self.sizes = {
139
- # "large_label": (0, 0),
140
- # "small_label": (0, 0),
141
- # "tiny_label": (0, 0),
142
- # "large": (0, 0),
143
- # "small": (0, 0),
144
- # "tiny": (0, 0),
145
- # }
146
-
147
- # def calc_sizes(self, dc):
148
- # def calc(bmap, uselabel):
149
- # w = 0
150
- # h = 0
151
- # return w, h
152
- # bw, bh = calc(self.bitmap_large, True)
153
- # self.sizes["large_label"] = (bw, bh)
154
- # bw, bh = calc(self.bitmap_large, False)
155
- # self.sizes["large"] = (bw, bh)
156
- # bw, bh = calc(self.bitmap_small, True)
157
- # self.sizes["small_label"] = (bw, bh)
158
- # bw, bh = calc(self.bitmap_small, False)
159
- # self.sizes["small"] = (bw, bh)
160
- # bw, bh = calc(self.bitmap_tiny, True)
161
- # self.sizes["tiny_label"] = (bw, bh)
162
- # bw, bh = calc(self.bitmap_tiny, False)
163
- # self.sizes["tiny"] = (bw, bh)
164
143
 
165
144
  def set_aspect(
166
145
  self,
@@ -195,63 +174,25 @@ class Button:
195
174
  @return:
196
175
  """
197
176
  self.label = label
198
- self.icon = icon
199
177
  resize_param = kwargs.get("size")
200
178
  if resize_param is None:
201
- resize_param = 50
202
- if resize_param is None:
203
- # We can get the real icon width, that means though
204
- # all buttons will have slightly different dimensions
205
- # so we set the minimum size
206
- siz = icon.GetBitmap().GetSize()
207
- wd = max(self.default_width, siz[0])
208
- small_resize = int(SMALL_RESIZE_FACTOR * wd)
209
- tiny_resize = int(TINY_RESIZE_FACTOR * wd)
210
- # print (f"No size parameter given for: {label}")
211
-
179
+ self.default_width = int(self.max_size / 2)
212
180
  else:
213
181
  self.default_width = resize_param
214
- small_resize = int(SMALL_RESIZE_FACTOR * resize_param)
215
- tiny_resize = int(TINY_RESIZE_FACTOR * resize_param)
216
182
 
217
- top = self.parent.parent.parent
218
- if top.art.dark_mode:
219
- targetcolor = Color("white")
220
- darkm = True
221
- else:
222
- targetcolor = None
223
- darkm = False
224
183
  # We need to cast the icon explicitly to PyEmbeddedImage
225
184
  # as otherwise a strange type error is thrown:
226
185
  # TypeError: GetBitmap() got an unexpected keyword argument 'force_darkmode'
227
186
  # Well...
187
+ from meerk40t.gui.icons import PyEmbeddedImage, VectorIcon
228
188
 
229
- icon = PyEmbeddedImage(icon.data)
230
- self.bitmap_large = icon.GetBitmap(
231
- resize=resize_param,
232
- noadjustment=True,
233
- force_darkmode=darkm,
234
- )
235
- self.bitmap_large_disabled = icon.GetBitmap(
236
- resize=resize_param,
237
- color=Color("grey"),
238
- noadjustment=True,
239
- force_darkmode=darkm,
240
- )
241
- self.bitmap_small = icon.GetBitmap(
242
- resize=small_resize, noadjustment=True, force_darkmode=darkm
243
- )
244
- self.bitmap_small_disabled = icon.GetBitmap(
245
- resize=small_resize, color=Color("grey"), noadjustment=True
246
- )
247
- self.bitmap_tiny = icon.GetBitmap(
248
- resize=tiny_resize, noadjustment=True, force_darkmode=darkm
249
- )
250
- self.bitmap_tiny_disabled = icon.GetBitmap(
251
- resize=tiny_resize, color=Color("grey"), noadjustment=True
252
- )
253
- self.bitmap = self.bitmap_large
254
- self.bitmap_disabled = self.bitmap_large_disabled
189
+ if not isinstance(icon, VectorIcon):
190
+ icon = PyEmbeddedImage(icon.data)
191
+ self.icon = icon
192
+
193
+ self.available_bitmaps.clear()
194
+ self.available_bitmaps_disabled.clear()
195
+ self.get_bitmaps(self.default_width)
255
196
 
256
197
  self.tip = tip
257
198
  self.group = group
@@ -269,6 +210,32 @@ class Button:
269
210
  self.dropdown = DropDown()
270
211
  self.modified()
271
212
 
213
+ def get_bitmaps(self, point_size):
214
+ top = self.parent.parent.parent
215
+ darkm = bool(top.art.color_mode == COLOR_MODE_DARK)
216
+ if point_size < self.min_size:
217
+ point_size = self.min_size
218
+ if point_size > self.max_size:
219
+ point_size = self.max_size
220
+ self.icon_size = int(point_size)
221
+ edge = int(point_size / 25.0) + 1
222
+ key = str(self.icon_size)
223
+ if key not in self.available_bitmaps:
224
+ self.available_bitmaps[key] = self.icon.GetBitmap(
225
+ resize=self.icon_size,
226
+ noadjustment=True,
227
+ force_darkmode=darkm,
228
+ buffer=edge,
229
+ )
230
+ self.available_bitmaps_disabled[key] = self.icon.GetBitmap(
231
+ resize=self.icon_size,
232
+ color=Color("grey"),
233
+ noadjustment=True,
234
+ buffer=edge,
235
+ )
236
+ self.bitmap = self.available_bitmaps[key]
237
+ self.bitmap_disabled = self.available_bitmaps_disabled[key]
238
+
272
239
  def _restore_button_aspect(self, key):
273
240
  """
274
241
  Restores a saved button aspect for the given key. Given a key to the alternative aspect we restore the given
@@ -432,7 +399,7 @@ class Button:
432
399
  if icon:
433
400
  # There seems to be a bug to display icons in a submenu consistently
434
401
  # print (f"Had a bitmap for {v.get('label')}")
435
- item.SetBitmap(icon.GetBitmap(resize=STD_ICON_SIZE / 2))
402
+ item.SetBitmap(icon.GetBitmap(resize=STD_ICON_SIZE / 2, buffer=2))
436
403
  top.Bind(wx.EVT_MENU, self.drop_menu_click(v), id=item.GetId())
437
404
  top.PopupMenu(menu)
438
405
 
@@ -589,6 +556,7 @@ class RibbonPanel:
589
556
 
590
557
  self.buttons = []
591
558
  self.position = None
559
+ self.available_position = None
592
560
  self._overflow = list()
593
561
  self._overflow_position = None
594
562
 
@@ -730,7 +698,7 @@ class RibbonPanel:
730
698
  item.Enable(v.enabled)
731
699
  item.SetHelp(v.tip)
732
700
  if v.icon:
733
- item.SetBitmap(v.icon.GetBitmap(resize=STD_ICON_SIZE / 2))
701
+ item.SetBitmap(v.icon.GetBitmap(resize=STD_ICON_SIZE / 2, buffer=2))
734
702
  top.Bind(wx.EVT_MENU, v.click, id=item.Id)
735
703
  top.PopupMenu(menu)
736
704
 
@@ -1059,12 +1027,59 @@ class RibbonBarPanel(wx.Control):
1059
1027
  """
1060
1028
  pos = event.Position
1061
1029
  button = self._button_at_position(pos)
1062
- if button is None:
1063
- return
1064
1030
  if button is not None:
1065
1031
  action = button.action_right
1066
1032
  if action:
1067
1033
  action(event)
1034
+ else:
1035
+ # Click on background, off menu to edit and set colors
1036
+ def set_color(newmode):
1037
+ self.context.root.ribbon_color = newmode
1038
+ # Force refresh
1039
+ self.context.signal("ribbon_recreate", None)
1040
+
1041
+ top = self # .parent
1042
+ c_mode = self.context.root.setting(int, "ribbon_color", COLOR_MODE_DEFAULT)
1043
+ menu = wx.Menu()
1044
+ item = menu.Append(wx.ID_ANY, _("Colorscheme"))
1045
+ item.Enable(False)
1046
+ item = menu.Append(wx.ID_ANY, _("System Default"), "", wx.ITEM_CHECK)
1047
+ item.Check(bool(c_mode == COLOR_MODE_DEFAULT))
1048
+ top.Bind(
1049
+ wx.EVT_MENU, lambda v: set_color(COLOR_MODE_DEFAULT), id=item.GetId()
1050
+ )
1051
+ item = menu.Append(wx.ID_ANY, _("Colored"), "", wx.ITEM_CHECK)
1052
+ item.Check(bool(c_mode == COLOR_MODE_COLOR))
1053
+ top.Bind(
1054
+ wx.EVT_MENU, lambda v: set_color(COLOR_MODE_COLOR), id=item.GetId()
1055
+ )
1056
+ item = menu.Append(wx.ID_ANY, _("Black"), "", wx.ITEM_CHECK)
1057
+ item.Check(bool(c_mode == COLOR_MODE_DARK))
1058
+ top.Bind(wx.EVT_MENU, lambda v: set_color(COLOR_MODE_DARK), id=item.GetId())
1059
+ item = menu.AppendSeparator()
1060
+ haslabel = self.art.show_labels
1061
+ item = menu.Append(wx.ID_ANY, _("Show Labels"), "", wx.ITEM_CHECK)
1062
+ if not getattr(self, "allow_labels", True):
1063
+ item.Enable(False)
1064
+ item.Check(haslabel)
1065
+ top.Bind(
1066
+ wx.EVT_MENU,
1067
+ lambda v: self.toggle_show_labels(not haslabel),
1068
+ id=item.GetId(),
1069
+ )
1070
+ item = menu.AppendSeparator()
1071
+ item = menu.Append(wx.ID_ANY, _("Customize Toolbars"))
1072
+
1073
+ def show_pref():
1074
+ self.context("window open Preferences\n")
1075
+ self.context.signal("preferences", "ribbon")
1076
+
1077
+ top.Bind(
1078
+ wx.EVT_MENU,
1079
+ lambda v: show_pref(),
1080
+ id=item.GetId(),
1081
+ )
1082
+ top.PopupMenu(menu)
1068
1083
 
1069
1084
  def on_click(self, event: wx.MouseEvent):
1070
1085
  """
@@ -1213,14 +1228,14 @@ class Art:
1213
1228
  self.text_dropdown_buffer = 7
1214
1229
  self.show_labels = True
1215
1230
 
1216
- self._establish_colors()
1231
+ self.establish_colors()
1217
1232
 
1218
1233
  self.current_page = None
1219
1234
  self.hover_tab = None
1220
1235
  self.hover_button = None
1221
1236
  self.hover_dropdown = None
1222
1237
 
1223
- def _establish_colors(self):
1238
+ def establish_colors(self):
1224
1239
  self.text_color = copy.copy(
1225
1240
  wx.SystemSettings().GetColour(wx.SYS_COLOUR_BTNTEXT)
1226
1241
  )
@@ -1230,12 +1245,12 @@ class Art:
1230
1245
  wx.SystemSettings().GetColour(wx.SYS_COLOUR_BTNTEXT)
1231
1246
  )
1232
1247
 
1233
- # self.button_face_hover = copy.copy(
1234
- # wx.SystemSettings().GetColour(wx.SYS_COLOUR_HIGHLIGHT)
1235
- # ).ChangeLightness(50)
1236
1248
  self.button_face_hover = copy.copy(
1237
- wx.SystemSettings().GetColour(wx.SYS_COLOUR_BTNHILIGHT)
1238
- ) # .ChangeLightness(25)
1249
+ wx.SystemSettings().GetColour(wx.SYS_COLOUR_HIGHLIGHT)
1250
+ ).ChangeLightness(150)
1251
+ # self.button_face_hover = copy.copy(
1252
+ # wx.SystemSettings().GetColour(wx.SYS_COLOUR_GRADIENTACTIVECAPTION)
1253
+ # )
1239
1254
  self.inactive_background = copy.copy(
1240
1255
  wx.SystemSettings().GetColour(wx.SYS_COLOUR_INACTIVECAPTION)
1241
1256
  )
@@ -1249,21 +1264,34 @@ class Art:
1249
1264
  wx.SystemSettings().GetColour(wx.SYS_COLOUR_INFOBK)
1250
1265
  )
1251
1266
  self.button_face = copy.copy(
1252
- wx.SystemSettings().GetColour(wx.SYS_COLOUR_BTNFACE)
1267
+ wx.SystemSettings().GetColour(wx.SYS_COLOUR_BTNHILIGHT)
1268
+ )
1269
+ self.ribbon_background = copy.copy(
1270
+ wx.SystemSettings().GetColour(wx.SYS_COLOUR_BTNHILIGHT)
1253
1271
  )
1254
1272
  self.highlight = copy.copy(
1255
1273
  wx.SystemSettings().GetColour(wx.SYS_COLOUR_HOTLIGHT)
1256
1274
  )
1257
1275
 
1258
- self.dark_mode = wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOW)[0] < 127
1259
- manualmode = self.parent.context.root.setting(bool, "force_darkmode", False)
1260
- if manualmode:
1261
- self.dark_mode = True
1276
+ # Do we have a setting for the color?
1277
+ c_mode = self.parent.context.root.setting(int, "ribbon_color", 0)
1278
+ # 0 system default
1279
+ # 1 colored background
1280
+ # 2 forced dark_mode
1281
+ if c_mode < COLOR_MODE_DEFAULT or c_mode > COLOR_MODE_DARK:
1282
+ c_mode = COLOR_MODE_DEFAULT
1283
+ if (
1284
+ c_mode == COLOR_MODE_DEFAULT
1285
+ and wx.SystemSettings().GetColour(wx.SYS_COLOUR_WINDOW)[0] < 127
1286
+ ):
1287
+ c_mode = COLOR_MODE_DARK # dark mode
1288
+ self.color_mode = c_mode
1262
1289
 
1263
- if self.dark_mode:
1290
+ if self.color_mode == COLOR_MODE_DARK:
1264
1291
  # This is rather crude, as a dark mode could also
1265
1292
  # be based eg on a dark blue scheme
1266
1293
  self.button_face = wx.BLACK
1294
+ self.ribbon_background = wx.BLACK
1267
1295
  self.text_color = wx.WHITE
1268
1296
  self.text_color_inactive = copy.copy(self.text_color)
1269
1297
  self.text_color_disabled = wx.Colour("Light Grey")
@@ -1272,15 +1300,30 @@ class Art:
1272
1300
  OS_NAME = platform.system()
1273
1301
  if OS_NAME == "Windows":
1274
1302
  self.button_face_hover = wx.BLUE
1275
-
1303
+ if self.color_mode == COLOR_MODE_COLOR:
1304
+ self.ribbon_background = copy.copy(
1305
+ wx.SystemSettings().GetColour(wx.SYS_COLOUR_GRADIENTINACTIVECAPTION)
1306
+ )
1307
+ self.button_face = copy.copy(
1308
+ wx.SystemSettings().GetColour(wx.SYS_COLOUR_GRADIENTACTIVECAPTION)
1309
+ )
1310
+ self.button_face_hover = wx.Colour("gold").ChangeLightness(150)
1311
+ self.highlight = wx.Colour("gold")
1312
+
1313
+ # Let's adjust the fontsize for the page headers
1314
+ screen_wd, screen_ht = wx.GetDisplaySize()
1315
+ ptdefault = 10
1316
+ if screen_wd <= 800 or screen_ht <= 600:
1317
+ ptdefault = 8
1318
+ self.tab_height = 16
1319
+ try:
1320
+ wxsize = wx.Size(ptdefault, ptdefault)
1321
+ dipsize = self.parent.FromDIP(wxsize)
1322
+ ptsize = int(dipsize[0])
1323
+ except AttributeError:
1324
+ ptsize = ptdefault
1276
1325
  self.default_font = wx.Font(
1277
- 10, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL
1278
- )
1279
- self.small_font = wx.Font(
1280
- 8, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL
1281
- )
1282
- self.tiny_font = wx.Font(
1283
- 6, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL
1326
+ ptsize, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL
1284
1327
  )
1285
1328
 
1286
1329
  def paint_main(self, dc, ribbon):
@@ -1306,13 +1349,14 @@ class Art:
1306
1349
  if page is not self.current_page or not page.visible:
1307
1350
  continue
1308
1351
 
1309
- dc.SetBrush(wx.Brush(self.button_face))
1352
+ dc.SetBrush(wx.Brush(self.ribbon_background))
1310
1353
  x, y, x1, y1 = page.position
1311
1354
  dc.DrawRoundedRectangle(
1312
1355
  int(x), int(y), int(x1 - x), int(y1 - y), self.rounded_radius
1313
1356
  )
1314
1357
  for panel in page.panels:
1315
- if panel is None:
1358
+ # We suppress empty panels
1359
+ if panel is None or panel.visible_button_count == 0:
1316
1360
  continue
1317
1361
  self._paint_panel(dc, panel)
1318
1362
  for button in panel.visible_buttons():
@@ -1368,7 +1412,7 @@ class Art:
1368
1412
  @return:
1369
1413
  """
1370
1414
  w, h = dc.Size
1371
- dc.SetBrush(wx.Brush(self.button_face))
1415
+ dc.SetBrush(wx.Brush(self.ribbon_background))
1372
1416
  dc.SetPen(wx.TRANSPARENT_PEN)
1373
1417
  dc.DrawRectangle(0, 0, w, h)
1374
1418
 
@@ -1384,7 +1428,7 @@ class Art:
1384
1428
  return
1385
1429
  x, y, x1, y1 = panel.position
1386
1430
  # print(f"Painting panel {panel.label}: {panel.position}")
1387
- dc.SetBrush(wx.Brush(self.button_face))
1431
+ dc.SetBrush(wx.Brush(self.ribbon_background))
1388
1432
  dc.SetPen(wx.Pen(self.black_color))
1389
1433
  dc.DrawRoundedRectangle(
1390
1434
  int(x), int(y), int(x1 - x), int(y1 - y), self.rounded_radius
@@ -1407,13 +1451,18 @@ class Art:
1407
1451
  cx = (x + x1) / 2
1408
1452
  cy = -r / 2 + (y + y1) / 2
1409
1453
  # print (f"area: {x},{y}-{x1},{y1} - center={cx},{cy} r={r}")
1410
- points = [
1411
- (
1412
- int(cx + r * math.cos(math.radians(angle))),
1413
- int(cy + r * math.sin(math.radians(angle))),
1414
- )
1415
- for angle in (0, 90, 180)
1416
- ]
1454
+ # points = [
1455
+ # (
1456
+ # int(cx + r * math.cos(math.radians(angle))),
1457
+ # int(cy + r * math.sin(math.radians(angle))),
1458
+ # )
1459
+ # for angle in (0, 90, 180)
1460
+ # ]
1461
+ lp_x = int(cx - r)
1462
+ lp_y = int(cy)
1463
+ dp_x = int(cx)
1464
+ dp_y = int(cy + r)
1465
+ points = [(lp_x, lp_y), (dp_x, dp_y), (2 * dp_x - lp_x, lp_y)]
1417
1466
  dc.SetPen(wx.Pen(self.black_color))
1418
1467
  dc.SetBrush(wx.Brush(self.inactive_background))
1419
1468
  dc.DrawPolygon(points)
@@ -1436,17 +1485,22 @@ class Art:
1436
1485
  dc.DrawRoundedRectangle(
1437
1486
  int(x), int(y), int(x1 - x), int(y1 - y), self.rounded_radius
1438
1487
  )
1439
- r = min((x1 - x) / 2, (y1 - y) / 2)
1488
+ r = min((y1 - y) / 2, (x1 - x) / 2) - 2
1440
1489
  cx = (x + x1) / 2
1441
1490
  cy = -r / 2 + (y + y1) / 2
1442
-
1443
- points = [
1444
- (
1445
- int(cx + r * math.cos(math.radians(x))),
1446
- int(cy + r * math.sin(math.radians(x))),
1447
- )
1448
- for x in (0, 90, 180)
1449
- ]
1491
+ # print (f"area: {x},{y}-{x1},{y1} - center={cx},{cy} r={r}")
1492
+ # points = [
1493
+ # (
1494
+ # int(cx + r * math.cos(math.radians(angle))),
1495
+ # int(cy + r * math.sin(math.radians(angle))),
1496
+ # )
1497
+ # for angle in (0, 90, 180)
1498
+ # ]
1499
+ lp_x = int(cx - r)
1500
+ lp_y = int(cy)
1501
+ dp_x = int(cx)
1502
+ dp_y = int(cy + r)
1503
+ points = [(lp_x, lp_y), (dp_x, dp_y), (2 * dp_x - lp_x, lp_y)]
1450
1504
  dc.SetPen(wx.Pen(self.black_color))
1451
1505
  dc.SetBrush(wx.Brush(self.inactive_background))
1452
1506
  dc.DrawPolygon(points)
@@ -1461,15 +1515,6 @@ class Art:
1461
1515
  """
1462
1516
  if button.overflow or not button.visible or button.position is None:
1463
1517
  return
1464
- bitmap = button.bitmap_large
1465
- bitmap_small = button.bitmap_small
1466
- bitmap_tiny = button.bitmap_tiny
1467
-
1468
- button.bitmapsize = "large"
1469
- if not button.enabled:
1470
- bitmap = button.bitmap_large_disabled
1471
- bitmap_small = button.bitmap_small_disabled
1472
- bitmap_tiny = button.bitmap_tiny_disabled
1473
1518
 
1474
1519
  dc.SetBrush(wx.Brush(self.button_face))
1475
1520
  dc.SetPen(wx.TRANSPARENT_PEN)
@@ -1488,104 +1533,98 @@ class Art:
1488
1533
  x, y, x1, y1 = button.position
1489
1534
  w = int(round(x1 - x, 2))
1490
1535
  h = int(round(y1 - y, 2))
1536
+ img_h = h
1537
+ # do we have text? if yes let's reduce the available space in y
1538
+ if self.show_labels: # Regardless whether we have a label or not...
1539
+ img_h -= self.bitmap_text_buffer
1540
+ ptsize = min(18, int(round(min(w, img_h) / 5.0, 2)) * 2)
1541
+ img_h -= int(ptsize * 1.35)
1542
+
1543
+ button.get_bitmaps(min(w, img_h))
1544
+ if button.enabled:
1545
+ bitmap = button.bitmap
1546
+ else:
1547
+ bitmap = button.bitmap_disabled
1491
1548
 
1492
- # Lets clip the output
1549
+ # Let's clip the output
1493
1550
  dc.SetClippingRegion(int(x), int(y), int(w), int(h))
1494
1551
 
1495
1552
  dc.DrawRoundedRectangle(int(x), int(y), int(w), int(h), self.rounded_radius)
1496
1553
  bitmap_width, bitmap_height = bitmap.Size
1497
- font = self.default_font
1498
1554
  # if button.label in ("Circle", "Ellipse", "Wordlist", "Property Window"):
1499
1555
  # print (f"N - {button.label}: {bitmap_width}x{bitmap_height} in {w}x{h}")
1500
- if bitmap_height > h or bitmap_width > w:
1501
- button.bitmapsize = "small"
1502
- bitmap = bitmap_small
1503
- font = self.small_font
1504
- bitmap_width, bitmap_height = bitmap.Size
1505
- # if button.label in ("Circle", "Ellipse", "Wordlist", "Property Window"):
1506
- # print (f"S - {button.label}: {bitmap_width}x{bitmap_height} in {w}x{h}")
1507
- if bitmap_height > h or bitmap_width > w:
1508
- button.bitmapsize = "tiny"
1509
- bitmap = bitmap_tiny
1510
- font = self.tiny_font
1511
- bitmap_width, bitmap_height = bitmap.Size
1512
- # if button.label in ("Circle", "Ellipse", "Wordlist", "Property Window"):
1513
- # print (f"T - {button.label}: {bitmap_width}x{bitmap_height} in {w}x{h}")
1556
+ bs = min(bitmap_width, bitmap_height)
1557
+ ptsize = self.get_font_size(bs)
1558
+ font = wx.Font(
1559
+ ptsize, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL
1560
+ )
1514
1561
 
1515
- bitmap_width, bitmap_height = bitmap.Size
1516
1562
  dc.DrawBitmap(bitmap, int(x + (w - bitmap_width) / 2), int(y))
1517
1563
  y += bitmap_height
1518
1564
 
1565
+ text_edge = self.bitmap_text_buffer
1566
+
1519
1567
  if button.label and self.show_labels:
1520
1568
  show_text = True
1521
1569
  label_text = list(button.label.split(" "))
1522
1570
  # We try to establish whether this would fit properly.
1523
1571
  # We allow a small oversize of 25% to the button,
1524
1572
  # before we try to reduce the fontsize
1525
- font_candidates = [
1526
- self.default_font,
1527
- self.small_font,
1528
- self.tiny_font,
1529
- ]
1530
- if button.bitmapsize == "tiny":
1531
- font_candidates = font_candidates[2:]
1532
- elif button.bitmapsize == "small":
1533
- font_candidates = font_candidates[1:]
1534
1573
  wouldfit = False
1535
- for testfont in font_candidates:
1536
- test_y = y + self.bitmap_text_buffer
1574
+ while not wouldfit:
1575
+ testfont = wx.Font(
1576
+ ptsize,
1577
+ wx.FONTFAMILY_SWISS,
1578
+ wx.FONTSTYLE_NORMAL,
1579
+ wx.FONTWEIGHT_NORMAL,
1580
+ )
1581
+ test_y = y + text_edge
1537
1582
  dc.SetFont(testfont)
1538
1583
  wouldfit = True
1539
- for word in label_text:
1584
+ i = 0
1585
+ while i < len(label_text):
1586
+ # We know by definition that all single words
1587
+ # are okay for drawing, now we check whether
1588
+ # we can draw multiple in one line
1589
+ word = label_text[i]
1590
+ cont = True
1591
+ while cont:
1592
+ cont = False
1593
+ if i < len(label_text) - 1:
1594
+ nextword = label_text[i + 1]
1595
+ test = word + " " + nextword
1596
+ tw, th = dc.GetTextExtent(test)
1597
+ if tw < w:
1598
+ word = test
1599
+ i += 1
1600
+ cont = True
1601
+
1540
1602
  text_width, text_height = dc.GetTextExtent(word)
1541
1603
  if text_width > w:
1542
1604
  wouldfit = False
1543
1605
  break
1606
+ test_y += text_height
1607
+ if test_y > y1:
1608
+ wouldfit = False
1609
+ text_edge = 0
1610
+ break
1611
+ i += 1
1612
+
1544
1613
  if wouldfit:
1545
1614
  font = testfont
1546
1615
  break
1616
+
1617
+ ptsize -= 2
1618
+ if ptsize < 6: # too small
1619
+ break
1547
1620
  if not wouldfit:
1548
1621
  show_text = False
1549
1622
  label_text = list()
1550
-
1551
- # Previous algorithm with abbreviated text
1552
- # test_y = y + self.bitmap_text_buffer
1553
- # dc.SetFont(font)
1554
- # for idx, word in enumerate(label_text):
1555
- # test_word = word
1556
- # while True:
1557
- # text_width, text_height = dc.GetTextExtent(test_word)
1558
- # if text_width <= w:
1559
- # break
1560
- # if test_word.endswith("..."):
1561
- # test_word = test_word[:-4]
1562
- # else:
1563
- # test_word = word[:-2]
1564
- # if len(test_word) > 0:
1565
- # test_word += "..."
1566
- # else:
1567
- # show_text = False
1568
- # break
1569
- # if len(test_word) > 0:
1570
- # if test_word.endswith("..."):
1571
- # label_text[idx] = test_word
1572
- # label_text = label_text[: idx + 1]
1573
- # break
1574
- # else:
1575
- # if idx == 0:
1576
- # show_text = False
1577
- # else:
1578
- # label_text = label_text[:idx]
1579
-
1580
- # break
1581
-
1582
- # test_y += text_height
1583
-
1584
1623
  else:
1585
1624
  show_text = False
1586
1625
  label_text = list()
1587
1626
  if show_text:
1588
- y += self.bitmap_text_buffer
1627
+ y += text_edge
1589
1628
  dc.SetFont(font)
1590
1629
  i = 0
1591
1630
  while i < len(label_text):
@@ -1631,6 +1670,7 @@ class Art:
1631
1670
  xpos = 0
1632
1671
  ypos = 0
1633
1672
  has_page_header = ribbon.visible_pages() > 1
1673
+ dc.SetFont(self.default_font)
1634
1674
  for pn, page in enumerate(ribbon.pages):
1635
1675
  if not page.visible:
1636
1676
  continue
@@ -1639,6 +1679,16 @@ class Art:
1639
1679
  # if bigger than default then extend width
1640
1680
  if has_page_header:
1641
1681
  line_width, line_height = dc.GetTextExtent(page.label)
1682
+ if line_height + 4 > self.tab_height:
1683
+ self.tab_height = line_height + 4
1684
+ for former in range(0, pn):
1685
+ former_page = ribbon.pages[former]
1686
+ t_x, t_y, t_x1, t_y1 = former_page.tab_position
1687
+ if horizontal:
1688
+ t_y1 = t_y + self.tab_height * 2
1689
+ else:
1690
+ t_x1 = t_x + self.tab_height * 2
1691
+ former_page.tab_position = (t_x, t_y, t_x1, t_y1)
1642
1692
 
1643
1693
  tabwidth = max(line_width + 2 * self.tab_tab_buffer, self.tab_width)
1644
1694
  if horizontal:
@@ -1709,22 +1759,6 @@ class Art:
1709
1759
  # print(f"page: {page.position}")
1710
1760
  self.page_layout(dc, page)
1711
1761
 
1712
- # def preferred_button_size_for_panel(self, dc, panel):
1713
- # # Provides
1714
- # x, y, max_x, max_y = panel.position
1715
- # panel_width = max_x - x
1716
- # panel_height = max_y - y
1717
- # button_sizes = []
1718
- # for button in panel.buttons:
1719
- # this_button_sizes = []
1720
- # # We calculate the space requirement for regular,
1721
- # # small and tiny buttons, both with and without
1722
- # # labels
1723
- # button_sizes.append(this_button_sizes)
1724
- # button_width = 0
1725
- # button_height = 0
1726
- # return button_width, button_height
1727
-
1728
1762
  def preferred_button_size_for_page(self, dc, page):
1729
1763
  x, y, max_x, max_y = page.position
1730
1764
  page_width = max_x - x
@@ -1814,9 +1848,37 @@ class Art:
1814
1848
  button_width, button_height = self.preferred_button_size_for_page(dc, page)
1815
1849
  x += self.page_panel_buffer
1816
1850
  y += self.page_panel_buffer
1851
+ """
1852
+ THIS ALGORITHM NEEDS STILL TO BE IMPLEMENTED
1853
+ --------------------------------------------
1854
+ We discuss now the horizontal case, the same
1855
+ logic would apply for the vertical case.
1856
+ We iterate through the sizes and establish the space
1857
+ needed for every panel:
1858
+ 1) We calculate the required button dimensions for all
1859
+ combinations of tiny/small/regular icons plus with/without labels
1860
+ 2) We get the minimum amount of columns required to display
1861
+ the buttons (taking the vertical extent ie the amount
1862
+ of available rows into account).
1863
+ This will provide us with a solution that would need
1864
+ the least horizontal space.
1865
+ 3) That may lead to a situation where you would still
1866
+ have horizontal space available for the panels.
1867
+ Hence we do a second pass where we assign additional space
1868
+ to all panels that need more than one row of icons.
1869
+ As we will do this for all possible size combinations,
1870
+ we will chose eventually that solution that has the
1871
+ fewest amount of buttons in overflow.
1872
+ """
1873
+ # 1 Calculate button sizes - this is not required
1874
+ # here, already done during button creation
1875
+
1876
+ # 2 Loop over all sizes
1877
+
1878
+ # Now that we have gathered all information we can assign
1879
+ # the space...
1880
+ available_space = 0
1817
1881
  for p, panel in enumerate(page.panels):
1818
- if panel.visible_button_count == 0:
1819
- continue
1820
1882
  if p != 0:
1821
1883
  # Non-first move between panel gap.
1822
1884
  if is_horizontal:
@@ -1841,11 +1903,35 @@ class Art:
1841
1903
  + (single_panel_vertical - 1) * self.between_button_buffer
1842
1904
  + 2 * self.panel_button_buffer
1843
1905
  )
1844
-
1906
+ if is_horizontal:
1907
+ sx = available_space
1908
+ sy = 0
1909
+ else:
1910
+ sx = 0
1911
+ sy = available_space
1912
+ panel_width += sx
1913
+ panel_height += sy
1845
1914
  panel.position = x, y, x + panel_width, y + panel_height
1846
- # if self.parent.visible_pages() == 1:
1847
- # print(f"panel: {panel.position}")
1848
- self.panel_layout(dc, panel)
1915
+ panel_max_x, panel_max_y = self.panel_layout(dc, panel)
1916
+ # Do we have more space than needed?
1917
+ available_space = 0
1918
+ if panel._overflow_position is None:
1919
+ # print (f"({x}, {y}) - ({x + panel_width}, {y+panel_height}), sx={sx}, sy={sy}")
1920
+ if is_horizontal:
1921
+ available_space = max(
1922
+ 0, x + panel_width - panel_max_x - self.panel_button_buffer
1923
+ )
1924
+ # print (f"x={x + panel_width}, {panel_max_x} will become: {panel_max_x + self.panel_button_buffer}, available={available_space}")
1925
+ if available_space != 0:
1926
+ panel_width = panel_max_x + self.panel_button_buffer - x
1927
+ else:
1928
+ available_space = max(
1929
+ 0, y + panel_height - panel_max_y - self.panel_button_buffer
1930
+ )
1931
+ # print (f"y={y + panel_height}, {panel_max_y} will become: {panel_max_y + self.panel_button_buffer}, available={available_space}")
1932
+ if available_space != 0:
1933
+ panel_height = panel_max_y + self.panel_button_buffer - y
1934
+ panel.position = x, y, x + panel_width, y + panel_height
1849
1935
 
1850
1936
  if is_horizontal:
1851
1937
  x += panel_width
@@ -1887,7 +1973,7 @@ class Art:
1887
1973
  button_width = all_button_width / button_horizontal
1888
1974
  button_height = all_button_height / button_vertical
1889
1975
  # 'tiny'-size of a default 50x50 icon
1890
- minim_size = int(TINY_RESIZE_FACTOR * 50)
1976
+ minim_size = 15
1891
1977
  button_width = max(minim_size, button_width)
1892
1978
  button_height = max(minim_size, button_height)
1893
1979
 
@@ -1897,13 +1983,13 @@ class Art:
1897
1983
  panel._overflow_position = None
1898
1984
  lastbutton = None
1899
1985
  for b, button in enumerate(list(panel.visible_buttons())):
1900
- button.bitmapsize = "large"
1901
- bitmap_width, bitmap_height = button.bitmap_large.Size
1902
- if bitmap_height > button_height or bitmap_width > button_width:
1903
- button.bitmapsize = "small"
1904
- bitmap_width, bitmap_height = button.bitmap_small.Size
1905
- if bitmap_height > button_height or bitmap_width > button_width:
1906
- button.bitmapsize = "tiny"
1986
+ found = False
1987
+ bitmapsize = button.max_size
1988
+ while bitmapsize > button.min_size:
1989
+ if bitmapsize <= button_height and bitmapsize <= button_width:
1990
+ break
1991
+ bitmapsize -= 5
1992
+ button.get_bitmaps(bitmapsize)
1907
1993
  self.button_calc(dc, button)
1908
1994
  if b == 0:
1909
1995
  max_width = button.min_width
@@ -1914,8 +2000,7 @@ class Art:
1914
2000
 
1915
2001
  target_height = button_height
1916
2002
  target_width = button_width
1917
- # if self.parent.visible_pages() == 1:
1918
- # print(f"Target: {target_width}x{target_height}")
2003
+ # print(f"Target: {panel.label} - {target_width}x{target_height}")
1919
2004
  for b, button in enumerate(list(panel.visible_buttons())):
1920
2005
  button.overflow = False
1921
2006
  this_width = target_width
@@ -1923,9 +2008,9 @@ class Art:
1923
2008
  local_width = 1.25 * button.min_width
1924
2009
  local_height = 1.25 * button.min_height
1925
2010
  if not distribute_evenly:
1926
- if button_horizontal > 1:
2011
+ if button_horizontal > 1 or is_horizontal:
1927
2012
  this_width = min(this_width, local_width)
1928
- if button_vertical > 1:
2013
+ if button_vertical > 1 or not is_horizontal:
1929
2014
  this_height = min(this_height, local_height)
1930
2015
  if b != 0:
1931
2016
  # Move across button gap if not first button.
@@ -2022,33 +2107,46 @@ class Art:
2022
2107
  else:
2023
2108
  y += this_height
2024
2109
  lastbutton = button
2110
+ return min(x, panel.position[2]), min(y, panel.position[3])
2025
2111
 
2026
2112
  def button_calc(self, dc: wx.DC, button):
2027
- default_w = button.default_width
2028
- if button.bitmapsize == "small":
2029
- default_w = int(SMALL_RESIZE_FACTOR * button.default_width)
2030
- bitmap = button.bitmap_small
2031
- dc.SetFont(self.small_font)
2032
- elif button.bitmapsize == "tiny":
2033
- default_w = int(TINY_RESIZE_FACTOR * button.default_width)
2034
- bitmap = button.bitmap_tiny
2035
- dc.SetFont(self.tiny_font)
2036
- else:
2037
- default_w = button.default_width
2038
- bitmap = button.bitmap_large
2039
- dc.SetFont(self.default_font)
2113
+ bitmap = button.bitmap
2114
+ ptsize = self.get_font_size(button.icon_size)
2115
+ font = wx.Font(
2116
+ ptsize, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL
2117
+ )
2118
+
2119
+ dc.SetFont(font)
2040
2120
  bitmap_width, bitmap_height = bitmap.Size
2041
- bitmap_height = max(bitmap_height, default_w)
2042
- bitmap_width = max(bitmap_width, default_w)
2121
+ bitmap_height = max(bitmap_height, button.icon_size)
2122
+ bitmap_width = max(bitmap_width, button.icon_size)
2043
2123
 
2044
2124
  # Calculate text height/width
2045
2125
  text_width = 0
2046
2126
  text_height = 0
2047
2127
  if button.label and self.show_labels:
2048
- for word in button.label.split(" "):
2128
+ label_text = list(button.label.split(" "))
2129
+ i = 0
2130
+ while i < len(label_text):
2131
+ # We know by definition that all single words
2132
+ # are okay for drawing, now we check whether
2133
+ # we can draw multiple in one line
2134
+ word = label_text[i]
2135
+ cont = True
2136
+ while cont:
2137
+ cont = False
2138
+ if i < len(label_text) - 1:
2139
+ nextword = label_text[i + 1]
2140
+ test = word + " " + nextword
2141
+ tw, th = dc.GetTextExtent(test)
2142
+ if tw < bitmap_width:
2143
+ word = test
2144
+ i += 1
2145
+ cont = True
2049
2146
  line_width, line_height = dc.GetTextExtent(word)
2050
2147
  text_width = max(text_width, line_width)
2051
2148
  text_height += line_height
2149
+ i += 1
2052
2150
 
2053
2151
  # Calculate button_width/button_height
2054
2152
  button_width = max(bitmap_width, text_width)
@@ -2065,12 +2163,7 @@ class Art:
2065
2163
 
2066
2164
  def button_layout(self, dc: wx.DC, button):
2067
2165
  x, y, max_x, max_y = button.position
2068
- if button.bitmapsize == "small":
2069
- bitmap = button.bitmap_small
2070
- elif button.bitmapsize == "tiny":
2071
- bitmap = button.bitmap_tiny
2072
- else:
2073
- bitmap = button.bitmap_large
2166
+ bitmap = button.bitmap
2074
2167
  bitmap_width, bitmap_height = bitmap.Size
2075
2168
  if button.kind == "hybrid" and button.key != "toggle":
2076
2169
  # Calculate text height/width
@@ -2100,3 +2193,18 @@ class Art:
2100
2193
  # f"Required for {button.label}: button: {x},{y} to {max_x},{max_y}," +
2101
2194
  # f"dropd: {extx-sizx},{exty-sizy} to {extx},{exty}"
2102
2195
  # )
2196
+
2197
+ def get_font_size(self, imgsize):
2198
+ if imgsize <= 20:
2199
+ ptsize = 6
2200
+ elif imgsize <= 30:
2201
+ ptsize = 8
2202
+ elif imgsize <= 40:
2203
+ ptsize = 10
2204
+ elif imgsize <= 60:
2205
+ ptsize = 12
2206
+ elif imgsize <= 80:
2207
+ ptsize = 14
2208
+ else:
2209
+ ptsize = 16
2210
+ return ptsize