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

Sign up to get free protection for your applications and to get access to all the features.
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