wxruby3-shapes 0.9.0.pre.beta.3 → 0.9.6

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/INSTALL.md +5 -7
  3. data/README.md +38 -6
  4. data/assets/logo.svg +339 -0
  5. data/assets/logo.xpm +60 -0
  6. data/assets/screenshot.png +0 -0
  7. data/assets/social.png +0 -0
  8. data/bin/wx-shapes +1 -1
  9. data/lib/wx/shapes/arrow_base.rb +4 -11
  10. data/lib/wx/shapes/arrows/circle_arrow.rb +22 -11
  11. data/lib/wx/shapes/arrows/circle_prong_arrow.rb +48 -0
  12. data/lib/wx/shapes/arrows/cross_bar_arrow.rb +57 -0
  13. data/lib/wx/shapes/arrows/cross_bar_circle_arrow.rb +56 -0
  14. data/lib/wx/shapes/arrows/cross_bar_prong_arrow.rb +49 -0
  15. data/lib/wx/shapes/arrows/crossed_circle.rb +46 -0
  16. data/lib/wx/shapes/arrows/cup_arrow.rb +65 -0
  17. data/lib/wx/shapes/arrows/diamond_arrow.rb +8 -13
  18. data/lib/wx/shapes/arrows/double_cross_bar_arrow.rb +27 -0
  19. data/lib/wx/shapes/arrows/filled_arrow.rb +60 -0
  20. data/lib/wx/shapes/arrows/line_arrow.rb +67 -0
  21. data/lib/wx/shapes/arrows/open_arrow.rb +22 -23
  22. data/lib/wx/shapes/arrows/prong_arrow.rb +42 -0
  23. data/lib/wx/shapes/arrows/solid_arrow.rb +21 -35
  24. data/lib/wx/shapes/arrows/square_arrow.rb +37 -0
  25. data/lib/wx/shapes/auto_layout.rb +2 -2
  26. data/lib/wx/shapes/base.rb +1 -1
  27. data/lib/wx/shapes/canvas_history.rb +20 -0
  28. data/lib/wx/shapes/connection_point.rb +10 -6
  29. data/lib/wx/shapes/diagram.rb +98 -78
  30. data/lib/wx/shapes/events.rb +8 -8
  31. data/lib/wx/shapes/printout.rb +3 -16
  32. data/lib/wx/shapes/serializable.rb +2 -436
  33. data/lib/wx/shapes/serialize/wx.rb +30 -18
  34. data/lib/wx/shapes/shape.rb +211 -168
  35. data/lib/wx/shapes/shape_canvas.rb +728 -267
  36. data/lib/wx/shapes/shape_data_object.rb +99 -18
  37. data/lib/wx/shapes/shape_handle.rb +18 -11
  38. data/lib/wx/shapes/shape_list.rb +34 -67
  39. data/lib/wx/shapes/shapes/bitmap_shape.rb +23 -24
  40. data/lib/wx/shapes/shapes/box_shape.rb +389 -0
  41. data/lib/wx/shapes/shapes/circle_shape.rb +19 -22
  42. data/lib/wx/shapes/shapes/control_shape.rb +77 -41
  43. data/lib/wx/shapes/shapes/curve_shape.rb +38 -31
  44. data/lib/wx/shapes/shapes/diamond_shape.rb +7 -17
  45. data/lib/wx/shapes/shapes/edit_text_shape.rb +6 -9
  46. data/lib/wx/shapes/shapes/ellipse_shape.rb +12 -15
  47. data/lib/wx/shapes/shapes/flex_grid_shape.rb +58 -33
  48. data/lib/wx/shapes/shapes/grid_shape.rb +259 -161
  49. data/lib/wx/shapes/shapes/line_shape.rb +155 -161
  50. data/lib/wx/shapes/shapes/manager_shape.rb +77 -0
  51. data/lib/wx/shapes/shapes/multi_sel_rect.rb +8 -8
  52. data/lib/wx/shapes/shapes/ortho_shape.rb +31 -36
  53. data/lib/wx/shapes/shapes/polygon_shape.rb +23 -29
  54. data/lib/wx/shapes/shapes/rect_shape.rb +95 -53
  55. data/lib/wx/shapes/shapes/round_ortho_shape.rb +6 -8
  56. data/lib/wx/shapes/shapes/round_rect_shape.rb +20 -24
  57. data/lib/wx/shapes/shapes/square_shape.rb +14 -17
  58. data/lib/wx/shapes/shapes/text_shape.rb +95 -53
  59. data/lib/wx/shapes/version.rb +1 -1
  60. data/lib/wx/shapes/wx.rb +16 -7
  61. data/lib/wx/wx-shapes/cmd/test.rb +1 -1
  62. data/samples/demo/arrows.json +1 -0
  63. data/samples/demo/arrows.yaml +793 -0
  64. data/samples/demo/art/HBox.xpm +22 -0
  65. data/samples/demo/art/VBox.xpm +22 -0
  66. data/samples/demo/art/logo.xpm +60 -0
  67. data/samples/demo/class.json +1 -0
  68. data/samples/demo/class.yaml +5631 -0
  69. data/samples/demo/demo.rb +301 -91
  70. data/samples/demo/dialogs.rb +1405 -0
  71. data/samples/demo/erd.json +1 -0
  72. data/samples/demo/erd.yaml +4072 -0
  73. data/samples/demo/frame_canvas.rb +409 -33
  74. data/samples/sample1/art/logo.xpm +60 -0
  75. data/samples/sample1/sample.rb +11 -11
  76. data/samples/sample2/art/logo.xpm +60 -0
  77. data/samples/sample2/sample.rb +2 -2
  78. data/samples/sample2/sample_shape.rb +15 -15
  79. data/samples/sample3/art/logo.xpm +60 -0
  80. data/samples/sample3/sample.rb +3 -3
  81. data/samples/sample4/art/logo.xpm +60 -0
  82. data/samples/sample4/sample.rb +2 -2
  83. data/tests/lib/wxapp_runner.rb +4 -0
  84. data/tests/serializer_tests.rb +8 -441
  85. data/tests/test_grid_shapes.rb +2 -2
  86. data/tests/test_serialize_xml.rb +17 -0
  87. data/tests/test_serialize_yaml.rb +2 -2
  88. metadata +78 -28
  89. data/lib/wx/shapes/serialize/core.rb +0 -40
  90. data/lib/wx/shapes/serialize/id.rb +0 -82
  91. data/lib/wx/shapes/serializer/json.rb +0 -258
  92. data/lib/wx/shapes/serializer/yaml.rb +0 -125
  93. data/samples/demo/art/sample.xpm +0 -251
  94. data/samples/sample1/art/sample.xpm +0 -251
  95. data/samples/sample2/art/sample.xpm +0 -251
  96. data/samples/sample3/art/sample.xpm +0 -251
  97. data/samples/sample4/art/sample.xpm +0 -251
@@ -0,0 +1,389 @@
1
+ # Wx::SF::BoxShape - box shape class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ require 'wx/shapes/shapes/rect_shape'
5
+ require 'wx/shapes/shapes/manager_shape'
6
+
7
+ module Wx::SF
8
+
9
+ # Class encapsulates a rectangular shape derived from Wx::SF::RectShape class which acts as a box-shaped
10
+ # container able to manage other assigned child shapes (it can control their position). The managed
11
+ # shapes are stacked into defined box (slots) according to it's primary orientation with a behaviour similar to
12
+ # classic Wx::BoxSizer class. The box will be automatically resized along it's primary axis to accommodate
13
+ # the combined sizes of the managed shapes. The minimum size of the box along it's secondary axis is
14
+ # determined by the maximum size of the managed shapes.
15
+ # When adding or removing shapes the stack of shapes will always be kept contiguous (without empty slots).
16
+ # Managed shapes will never be resized along the primary axis but may be resized and/or positioned along
17
+ # the secondary axis according to the contained shape's alignment setting (EXPAND).
18
+ class BoxShape < RectShape
19
+
20
+ include ManagerShape
21
+
22
+ # Orientation values
23
+ class ORIENTATION < Wx::Enum
24
+ HORIZONTAL = self.new(0)
25
+ VERTICAL = self.new(1)
26
+ end
27
+
28
+ # Default values
29
+ class DEFAULT
30
+ # default box orientation
31
+ ORIENTATION = ORIENTATION::HORIZONTAL
32
+ # Default value of GridShape @cell_space data member.
33
+ SPACING = 3
34
+ end
35
+
36
+ class << self
37
+
38
+ # Returns the minimum size for *empty* boxes
39
+ # @return [Wx::Size]
40
+ def get_min_size
41
+ @min_size ||= Wx::Size.new(20, 20)
42
+ end
43
+ alias :min_size :get_min_size
44
+
45
+ # Sets the minimum size for *empty* boxes
46
+ # @overload set_min_size(sz)
47
+ # @param [Wx::Size] sz
48
+ # @overload set_min_size(w, h)
49
+ # @param [Integer] w
50
+ # @param [Integer] h
51
+ def set_min_size(arg1, arg2 = nil)
52
+ @min_size = if arg2.nil?
53
+ raise ArgumentError, 'Expected Wx::Size' unless Wx::Size === arg1
54
+ arg1
55
+ else
56
+ Wx::Size.new(arg1, arg2)
57
+ end
58
+ end
59
+ alias :min_size= :set_min_size
60
+
61
+ end
62
+
63
+ property :spacing, :orientation, :slots
64
+
65
+ # Constructor.
66
+ # @param [Wx::RealPoint,Wx::Point] pos Initial position
67
+ # @param [Wx::RealPoint,Wx::Point,Wx::Size] size Initial size
68
+ # @param [Wx::SF::BoxShape::ORIENTATION] orientation box orientation
69
+ # @param [Integer] spacing Additional space between managed shapes
70
+ # @param [Wx::SF::Diagram] diagram parent diagram
71
+ def initialize(pos = Shape::DEFAULT::POSITION, size = RectShape::DEFAULT::SIZE, orientation: DEFAULT::ORIENTATION, spacing: DEFAULT::SPACING, diagram: nil)
72
+ super(pos, size, diagram: diagram)
73
+ @orientation = orientation || DEFAULT::ORIENTATION
74
+ @spacing = spacing || 0
75
+ @slots = []
76
+ end
77
+
78
+ # Get the box shape orientation.
79
+ # @return [Wx::SF::BoxShape::ORIENTATION]
80
+ def get_orientation
81
+ @orientation
82
+ end
83
+ alias :orientation :get_orientation
84
+
85
+ # Get number of filled slots (i.e. managed shapes)
86
+ # @return [Integer]
87
+ def get_slot_count
88
+ @slots.size
89
+ end
90
+ alias :slot_count :get_slot_count
91
+
92
+ # Set space between slots (managed shapes).
93
+ # @param [Integer] spacing Spacing size
94
+ # @return [Integer] new spacing size
95
+ def set_spacing(spacing)
96
+ @spacing = spacing
97
+ end
98
+ alias :spacing= :set_spacing
99
+
100
+ # Get space between slots (managed shapes).
101
+ # @return [Integer] Spacing size
102
+ def get_spacing
103
+ @spacing
104
+ end
105
+ alias :spacing :get_spacing
106
+
107
+ # Iterate all slots. If a block is given passes slot index and shape for each slot to block.
108
+ # Returns Enumerator if no block given.
109
+ # @overload each_slot()
110
+ # @return [Enumerator]
111
+ # @overload each_slot(&block)
112
+ # @yieldparam [Integer] slot
113
+ # @yieldparam [Shape,nil] shape
114
+ # @return [Object]
115
+ def each_slot(&block)
116
+ if block
117
+ @slots.each_with_index do |shape, slot|
118
+ block.call(slot, shape)
119
+ end
120
+ else
121
+ ::Enumerator.new do |y|
122
+ @slots.each_with_index do |shape, slot|
123
+ y << [slot, shape]
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ # Get managed shape specified by slot index.
130
+ # @param [Integer] slot slot index of requested shape
131
+ # @return [Shape, nil] shape object of given slot if exists, otherwise nil
132
+ def get_managed_shape(slot)
133
+ @slots[slot]
134
+ end
135
+
136
+ # Clear information about managed shapes and remove all slots.
137
+ #
138
+ # Note that this function doesn't remove managed (child) shapes from the parent box shape
139
+ # (they are still its child shapes but aren't managed anymore).
140
+ def clear_box
141
+ @slots.clear
142
+ end
143
+
144
+ # Append given shape to the box at the last managed position.
145
+ # @param [Shape] shape shape to append
146
+ # @return [Boolean] true on success, otherwise false
147
+ def append_to_box(shape)
148
+ insert_to_box(@slots.size, shape)
149
+ end
150
+
151
+ # Insert given shape to the box at the given position.
152
+ # The given shape is inserted before the existing item at index 'slot', thus insert_to_box(0, something)
153
+ # will insert an item in such way that it will become the first box element. Any occupied slots at given
154
+ # position or beyond will be shifted to the next position.
155
+ # @param [Integer] slot slot index for inserted shape
156
+ # @param [Shape] shape shape to insert
157
+ # @return [Boolean] true on success, otherwise false
158
+ def insert_to_box(slot, shape)
159
+ if shape && shape.is_a?(Shape) && is_child_accepted(shape.class)
160
+ # protect duplicated occurrences
161
+ return false if @slots.index(shape)
162
+
163
+ # protect unbounded index
164
+ return false if slot > @slots.size
165
+
166
+ # add the shape to the children list if necessary
167
+ unless @child_shapes.include?(shape)
168
+ if @diagram
169
+ @diagram.reparent_shape(shape, self)
170
+ else
171
+ shape.set_parent_shape(self)
172
+ end
173
+ end
174
+
175
+ @slots.insert(slot, shape)
176
+
177
+ return true
178
+ end
179
+ false
180
+ end
181
+
182
+ # Remove given shape from the box.
183
+ # Shifts any occupied cells beyond the slots containing the given shape to the previous position.
184
+ # @param [Shape] shape shape which should be removed
185
+ # @note Note this does *not* remove the shape as a child shape.
186
+ def remove_from_box(shape)
187
+ @slots.delete(shape)
188
+ end
189
+
190
+ # Update shape (align all child shapes and resize it to fit them)
191
+ def update(recurse = true)
192
+ # check for stale links to of de-assigned shapes
193
+ @slots.delete_if do |shape|
194
+ !@child_shapes.include?(shape)
195
+ end
196
+
197
+ # check whether all child shapes are present in the slots array...
198
+ @child_shapes.each do |child|
199
+ unless @slots.include?(child)
200
+ # see if we can match the position of the new child with the position of another
201
+ # (previously assigned) managed shape
202
+ find_child_slot(child)
203
+ end
204
+ end
205
+
206
+ # do self-alignment
207
+ do_alignment
208
+
209
+ # fit the shape to its children
210
+ fit_to_children unless has_style?(STYLE::NO_FIT_TO_CHILDREN)
211
+
212
+ # do it recursively on all parent shapes
213
+ if recurse && (parent = get_parent_shape)
214
+ parent.update(recurse)
215
+ end
216
+ end
217
+
218
+ # Resize the shape to bound all child shapes. The function can be overridden if necessary.
219
+ def fit_to_children
220
+ # get bounding box of the shape and children set to be inside it
221
+ abs_pos = get_absolute_position
222
+ ch_bb = Wx::Rect.new(abs_pos.to_point, [0, 0])
223
+
224
+ @child_shapes.each do |child|
225
+ ch_bb = child.get_complete_bounding_box(ch_bb, BBMODE::SELF | BBMODE::CHILDREN) if child.has_style?(STYLE::ALWAYS_INSIDE)
226
+ end
227
+
228
+ if @child_shapes.empty?
229
+ # do not let the empty box shape 'disappear' due to zero sizes...
230
+ ch_bb.width = get_h_align == HALIGN::EXPAND ? @rect_size.x.to_i-@spacing : GridShape.min_size.width
231
+ ch_bb.height = get_v_align == VALIGN::EXPAND ? @rect_size.y.to_i-@spacing : GridShape.min_size.height
232
+ end
233
+
234
+ @rect_size = Wx::RealPoint.new(ch_bb.width + @spacing, ch_bb.height + @spacing)
235
+ end
236
+
237
+ # Event handler called when any shape is dropped above this shape (and the dropped
238
+ # shape is accepted as a child of this shape). The function can be overridden if necessary.
239
+ #
240
+ # The function is called by the framework (by the shape canvas).
241
+ # @param [Wx::RealPoint] _pos Relative position of dropped shape
242
+ # @param [Shape] child dropped shape
243
+ def on_child_dropped(_pos, child)
244
+ # see if we can match the position of the new child with the position of another
245
+ # (previously assigned) managed shape
246
+ if child && !child.is_a?(LineShape)
247
+ # if the child already had a slot
248
+ if @slots.index(child)
249
+ # remove it from there; this provides support for reordering child shapes by dragging
250
+ remove_from_box(child)
251
+ end
252
+
253
+ # insert child based on it's current (possibly dropped) position
254
+ find_child_slot(child)
255
+ end
256
+ end
257
+
258
+ protected
259
+
260
+ # Do layout of assigned child shapes
261
+ def do_children_layout
262
+ return if @slots.empty?
263
+
264
+ max_size = 0
265
+
266
+ # get maximum size of all managed (child) shapes
267
+ @child_shapes.each do |shape|
268
+ curr_rect = shape.get_bounding_box
269
+ curr_rect.inflate!(shape.h_border.abs.to_i, shape.v_border.abs.to_i)
270
+
271
+ if @orientation == ORIENTATION::VERTICAL
272
+ max_size = curr_rect.width if shape.get_h_align != HALIGN::EXPAND && curr_rect.width > max_size
273
+ else
274
+ max_size = curr_rect.height if shape.get_v_align != VALIGN::EXPAND && curr_rect.height > max_size
275
+ end
276
+ end
277
+
278
+ # if this box itself is expanded for the appropriate dimension check the max child size against the box size
279
+ if @orientation == ORIENTATION::VERTICAL && get_h_align == HALIGN::EXPAND
280
+ box_rect = get_bounding_box
281
+ # if this box is horizontally expanded use it's width if larger
282
+ max_size = box_rect.width - 2*@spacing if (box_rect.width-2*@spacing) > max_size
283
+ elsif @orientation == ORIENTATION::HORIZONTAL && get_v_align == VALIGN::EXPAND
284
+ box_rect = get_bounding_box
285
+ # if this box is vertically expanded use it's height if larger
286
+ max_size = box_rect.height - 2*@spacing if (box_rect.height-2*@spacing) > max_size
287
+ end
288
+
289
+ offset = @spacing
290
+ @slots.each do |shape|
291
+ if @orientation == ORIENTATION::VERTICAL
292
+ shape_h = shape.get_bounding_box.height + (2*shape.get_v_border).to_i
293
+ fit_shape_to_rect(shape, Wx::Rect.new(@spacing,
294
+ offset,
295
+ max_size, shape_h))
296
+ offset += shape_h+@spacing
297
+ else
298
+ shape_w = shape.get_bounding_box.width + (2*shape.get_h_border).to_i
299
+ fit_shape_to_rect(shape, Wx::Rect.new(offset,
300
+ @spacing,
301
+ shape_w, max_size))
302
+ offset += shape_w+@spacing
303
+ end
304
+ end
305
+ end
306
+
307
+ # called after the shape has been newly imported/pasted/dropped
308
+ # checks the slots for stale links
309
+ def on_import
310
+ # check for existence of non-included shapes
311
+ @slots.delete_if do |shape|
312
+ !@child_shapes.include?(shape)
313
+ end
314
+ end
315
+
316
+ def find_child_slot(child)
317
+ crct = child.get_bounding_box
318
+ # if the child intersects this box shape we look
319
+ # for the slot it should go into
320
+ if intersects?(crct)
321
+ # find the slot with a shape that is positioned below/after
322
+ # the new child
323
+ slot = @slots.find_index do |shape|
324
+ # determine if new child is positioned above/in front of existing child shape
325
+ srct = shape.get_bounding_box
326
+ if @orientation == ORIENTATION::VERTICAL
327
+ crct.bottom <= srct.bottom || crct.top <= srct.top
328
+ else
329
+ crct.right <= srct.right || crct.left <= srct.left
330
+ end
331
+ end
332
+ if slot # if found
333
+ # insert before other shape
334
+ @slots.insert(slot, child)
335
+ return
336
+ end
337
+ end
338
+ # otherwise append
339
+ @slots << child
340
+ end
341
+
342
+ private
343
+
344
+ # Deserialization only.
345
+
346
+ # Set the orientation of the box shape.
347
+ # @param [Wx::SF::BoxShape::ORIENTATION] orientation
348
+ def set_orientation(orientation)
349
+ @orientation = orientation
350
+ end
351
+
352
+ def get_slots
353
+ @slots
354
+ end
355
+ def set_slots(slots)
356
+ @slots = slots
357
+ end
358
+
359
+ end
360
+
361
+ # Convenience class encapsulating a BoxShape with vertical orientation.
362
+ class VBoxShape < BoxShape
363
+
364
+ # Constructor.
365
+ # @param [Wx::RealPoint,Wx::Point] pos Initial position
366
+ # @param [Wx::RealPoint,Wx::Point,Wx::Size] size Initial size
367
+ # @param [Integer] spacing Additional space between managed shapes
368
+ # @param [Wx::SF::Diagram] diagram parent diagram
369
+ def initialize(pos = Shape::DEFAULT::POSITION, size = RectShape::DEFAULT::SIZE, spacing: DEFAULT::SPACING, diagram: nil)
370
+ super(pos, size, orientation: ORIENTATION::VERTICAL, spacing: spacing, diagram: diagram)
371
+ end
372
+
373
+ end
374
+
375
+ # Convenience class encapsulating a BoxShape with horizontal orientation.
376
+ class HBoxShape < BoxShape
377
+
378
+ # Constructor.
379
+ # @param [Wx::RealPoint,Wx::Point] pos Initial position
380
+ # @param [Wx::RealPoint,Wx::Point,Wx::Size] size Initial size
381
+ # @param [Integer] spacing Additional space between managed shapes
382
+ # @param [Wx::SF::Diagram] diagram parent diagram
383
+ def initialize(pos = Shape::DEFAULT::POSITION, size = RectShape::DEFAULT::SIZE, spacing: DEFAULT::SPACING, diagram: nil)
384
+ super(pos, size, orientation: ORIENTATION::HORIZONTAL, spacing: spacing, diagram: diagram)
385
+ end
386
+
387
+ end
388
+
389
+ end
@@ -8,21 +8,18 @@ module Wx::SF
8
8
  # Class encapsulating the circle shape.
9
9
  class CircleShape < SquareShape
10
10
 
11
- # @overload initialize()
12
- # Default constructor.
13
- # @overload initialize(pos, size, diagram)
14
- # User constructor.
15
- # @param [Wx::RealPoint] pos Initial position
16
- # @param [Float] radius Initial circle radius
17
- # @param [Wx::SF::Diagram] diagram parent diagram
18
- def initialize(*args)
19
- if args.empty?
20
- super
21
- set_rect_size(50,50)
22
- else
23
- pos, rad, diagram = args
24
- super(pos, rad*2, diagram)
25
- end
11
+ # Default values
12
+ module DEFAULT
13
+ # Default circle radius
14
+ RADIUS = 25.0
15
+ end
16
+
17
+ # Constructor.
18
+ # @param [Wx::RealPoint] pos Initial position
19
+ # @param [Float] radius Initial circle radius
20
+ # @param [Wx::SF::Diagram] diagram parent diagram
21
+ def initialize(pos = Shape::DEFAULT::POSITION, radius = DEFAULT::RADIUS, diagram: nil)
22
+ super(pos, radius*2, diagram: diagram)
26
23
  end
27
24
 
28
25
  def get_radius
@@ -72,8 +69,8 @@ module Wx::SF
72
69
  # HINT: overload it for custom actions...
73
70
  pos = get_absolute_position
74
71
 
75
- dc.with_pen(@border) do
76
- dc.with_brush(@fill) do
72
+ dc.with_pen(border) do
73
+ dc.with_brush(fill) do
77
74
  dc.draw_circle((pos.x + @rect_size.x/2).to_i,
78
75
  (pos.y + @rect_size.y/2).to_i,
79
76
  (@rect_size.x/2).to_i)
@@ -88,8 +85,8 @@ module Wx::SF
88
85
  # HINT: overload it for custom actions...
89
86
  pos = get_absolute_position
90
87
 
91
- dc.with_pen(Wx::Pen.new(@hover_color, 1)) do
92
- dc.with_brush(@fill) do
88
+ dc.with_pen(Wx::Pen.new(hover_colour, 1)) do
89
+ dc.with_brush(fill) do
93
90
  dc.draw_circle((pos.x + @rect_size.x/2).to_i,
94
91
  (pos.y + @rect_size.y/2).to_i,
95
92
  (@rect_size.x/2).to_i)
@@ -105,8 +102,8 @@ module Wx::SF
105
102
  # HINT: overload it for custom actions...
106
103
  pos = get_absolute_position
107
104
 
108
- dc.with_pen(Wx::Pen.new(@hover_color, 2)) do
109
- dc.with_brush(@fill) do
105
+ dc.with_pen(Wx::Pen.new(hover_colour, 2)) do
106
+ dc.with_brush(fill) do
110
107
  dc.draw_circle((pos.x + @rect_size.x/2).to_i,
111
108
  (pos.y + @rect_size.y/2).to_i,
112
109
  (@rect_size.x/2).to_i)
@@ -120,7 +117,7 @@ module Wx::SF
120
117
  # HINT: overload it for custom actions...
121
118
  pos = get_absolute_position
122
119
 
123
- if @fill.style != Wx::BrushStyle::BRUSHSTYLE_TRANSPARENT
120
+ if fill.style != Wx::BrushStyle::BRUSHSTYLE_TRANSPARENT
124
121
  dc.with_pen(Wx::TRANSPARENT_PEN) do
125
122
  dc.with_brush(get_parent_canvas.get_shadow_fill) do
126
123
  dc.draw_circle((pos.x + @rect_size.x/2 + get_parent_canvas.get_shadow_offset.x).to_i,
@@ -123,35 +123,29 @@ module Wx::SF
123
123
  module DEFAULT
124
124
  CONTROLOFFSET = 0
125
125
  PROCESSEVENTS = EVTPROCESSING::KEY2CANVAS | EVTPROCESSING::MOUSE2CANVAS
126
- MODFILL = Wx::Brush.new(Wx::BLUE, Wx::BrushStyle::BRUSHSTYLE_BDIAGONAL_HATCH) if Wx::App.is_main_loop_running
127
- Wx.add_delayed_constant(self, :MODFILL) { Wx::Brush.new(Wx::BLUE, Wx::BrushStyle::BRUSHSTYLE_BDIAGONAL_HATCH) }
128
- MODBORDER = Wx::Pen.new(Wx::BLUE, 1, Wx::PenStyle::PENSTYLE_SOLID) if Wx::App.is_main_loop_running
129
- Wx.add_delayed_constant(self, :MODBORDER) { Wx::Pen.new(Wx::BLUE, 1, Wx::PenStyle::PENSTYLE_SOLID) }
130
- end
131
-
132
- property :event_processing, :control_offset, :mod_fill, :mod_border
133
-
134
- # @overload initialize()
135
- # Default constructor.
136
- # @overload initialize(pos, size, diagram)
137
- # User constructor.
138
- # @param [Wx::Window] ctrl managed GUI control
139
- # @param [Wx::RealPoint] pos Initial position
140
- # @param [Wx::RealPoint] size Initial size
141
- # @param [Wx::SF::Diagram] diagram parent diagram
142
- def initialize(*args)
143
- if args.empty?
144
- super
145
- @control = nil
146
- else
147
- ctrl = args.shift
148
- super(*args)
149
- set_control(ctrl)
126
+ class << self
127
+ def mod_fill; @mod_fill ||= Wx::Brush.new(Wx::BLUE, Wx::BrushStyle::BRUSHSTYLE_BDIAGONAL_HATCH); end
128
+ def mod_border; @mod_border ||= Wx::Pen.new(Wx::BLUE, 1, Wx::PenStyle::PENSTYLE_SOLID); end
129
+ def ctrl_fill; @ctrl_fill ||= Wx::TRANSPARENT_BRUSH.dup; end
130
+ def ctrl_border; @ctrl_border ||= Wx::TRANSPARENT_PEN.dup; end
150
131
  end
132
+ end
133
+
134
+ property :event_processing, :control_offset
135
+ property({ mod_fill: :serialize_mod_fill, mod_border: :serialize_mod_border }, optional: true)
136
+
137
+ # Constructor.
138
+ # @param [Wx::RealPoint] pos Initial position
139
+ # @param [Wx::RealPoint] size Initial size
140
+ # @param [Wx::Window] control managed GUI control
141
+ # @param [Wx::SF::Diagram] diagram parent diagram
142
+ def initialize(pos = Shape::DEFAULT::POSITION, size = RectShape::DEFAULT::SIZE, control: nil, diagram: nil)
143
+ super(pos, size, diagram: diagram)
144
+ set_control(control)
151
145
  add_style(Shape::STYLE::PROCESS_DEL)
152
146
  @process_events = DEFAULT::PROCESSEVENTS
153
- @mod_fill = DEFAULT::MODFILL
154
- @mod_border = DEFAULT::MODBORDER
147
+ @mod_fill = nil
148
+ @mod_border = nil
155
149
  @control_offset = DEFAULT::CONTROLOFFSET
156
150
 
157
151
  @event_sink = EventSink.new(self)
@@ -160,9 +154,6 @@ module Wx::SF
160
154
  @prev_style = 0
161
155
  @prev_fill = nil
162
156
  @prev_border = nil
163
-
164
- @fill = Wx::TRANSPARENT_BRUSH
165
- @border = Wx::TRANSPARENT_PEN
166
157
  end
167
158
 
168
159
  # Set managed GUI control.
@@ -211,6 +202,20 @@ module Wx::SF
211
202
  end
212
203
  end
213
204
 
205
+ # Get current fill style.
206
+ # @return [Wx::Brush] Current brush
207
+ def get_fill
208
+ @fill || (@diagram&.shape_canvas ? @diagram.shape_canvas.control_fill : DEFAULT.ctrl_fill)
209
+ end
210
+ alias :fill :get_fill
211
+
212
+ # Get current border style.
213
+ # @return [Wx::Pen] Current pen
214
+ def get_border
215
+ @border || (@diagram&.shape_canvas ? @diagram.shape_canvas.control_border : DEFAULT.ctrl_border)
216
+ end
217
+ alias :border :get_border
218
+
214
219
  # Get managed GUI control.
215
220
  # @return [Wx::Window] the GUI control
216
221
  def get_control
@@ -232,27 +237,46 @@ module Wx::SF
232
237
  end
233
238
 
234
239
  # Set control shape's background style used during its modification.
235
- # @param [Wx::Brush] brush Reference to used brush
236
- def set_mod_fill(brush)
237
- @mod_fill = brush
240
+ # @overload set_mod_fill(brush)
241
+ # @param [Wx::Brush] brush
242
+ # @overload set_mod_fill(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID)
243
+ # @param [Wx::Colour,Symbol,String] color brush color
244
+ # @param [Wx::BrushStyle] style
245
+ # @overload set_mod_fill(stipple_bitmap)
246
+ # @param [Wx::Bitmap] stipple_bitmap
247
+ def set_mod_fill(*args)
248
+ @mod_fill = if args.size == 1 && Wx::Brush === args.first
249
+ args.first
250
+ else
251
+ Wx::Brush.new(*args)
252
+ end
238
253
  end
239
254
 
240
255
  # Get control shape's background style used during its modification.
241
256
  # @return [Wx::Brush] Used brush
242
257
  def get_mod_fill
243
- @mod_fill
258
+ @mod_fill || (@diagram&.shape_canvas ? @diagram.shape_canvas.control_mod_fill : DEFAULT.mod_fill)
244
259
  end
245
260
 
246
261
  # Set control shape's border style used during its modification.
247
- # @param [Wx::Pen] pen Reference to used pen
262
+ # @overload set_mod_border(pen)
263
+ # @param [Wx::Pen] pen
264
+ # @overload set_mod_border(color, width=1, style=Wx::PenStyle::PENSTYLE_SOLID)
265
+ # @param [Wx::Colour,String,Symbol] color
266
+ # @param [Integer] width
267
+ # @param [Wx::PenStyle] style
248
268
  def set_mod_border(pen)
249
- @mod_border = pen
269
+ @mod_border = if args.size == 1 && Wx::Pen === args.first
270
+ args.first
271
+ else
272
+ Wx::Pen.new(*args)
273
+ end
250
274
  end
251
275
 
252
276
  # Get control shape's border style used during its modification.
253
277
  # @return [Wx::Pen] Used pen
254
278
  def get_mod_border
255
- @mod_border
279
+ @mod_border || (@diagram&.shape_canvas ? @diagram.shape_canvas.control_mod_border : DEFAULT.mod_border)
256
280
  end
257
281
 
258
282
  # Set control shape's offset (a gap between the shape's border and managed GUI control).
@@ -315,7 +339,7 @@ module Wx::SF
315
339
  # @param [Float] x Horizontal scale factor
316
340
  # @param [Float] y Vertical scale factor
317
341
  # @param children true if the shape's children should be scaled as well, otherwise the shape will be updated after scaling via update() function.
318
- def scale(x, y, children = WITHCHILDREN)
342
+ def scale(x, y, children: WITHCHILDREN)
319
343
  super
320
344
  update_control
321
345
  end
@@ -337,7 +361,7 @@ module Wx::SF
337
361
  end
338
362
 
339
363
  # Update shape (align all child shapes an resize it to fit them)
340
- def update
364
+ def update(recurse = true)
341
365
  super
342
366
  update_control
343
367
  end
@@ -365,7 +389,7 @@ module Wx::SF
365
389
  # @see ShapeCanvas
366
390
  def on_begin_drag(pos)
367
391
  @prev_fill = @fill
368
- @fill = @mod_fill
392
+ @fill = get_mod_fill
369
393
  canvas = get_parent_canvas
370
394
  if canvas
371
395
  @prev_style = canvas.get_style
@@ -406,9 +430,9 @@ module Wx::SF
406
430
  # @param [Shape::Handle] handle Reference to dragged handle
407
431
  def on_begin_handle(handle)
408
432
  @prev_border = @border
409
- @border = @mod_border
433
+ @border = get_mod_border
410
434
  @prev_fill = @fill
411
- @fill = @mod_fill
435
+ @fill = get_mod_fill
412
436
 
413
437
  if @control
414
438
  @control.hide
@@ -478,6 +502,18 @@ module Wx::SF
478
502
  end
479
503
  end
480
504
 
505
+ # (de-)serialize mod_fill; allows for nil values
506
+ def serialize_mod_fill(*val)
507
+ @mod_fill = val.first unless val.empty?
508
+ @mod_fill
509
+ end
510
+
511
+ # (de-)serialize mod_fill; allows for nil values
512
+ def serialize_mod_border(*val)
513
+ @mod_border = val.first unless val.empty?
514
+ @mod_border
515
+ end
516
+
481
517
  end
482
518
 
483
519
  end