wxruby3-shapes 0.9.0.pre.beta.3
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.
- checksums.yaml +7 -0
- data/.yardopts +12 -0
- data/CREDITS.md +18 -0
- data/INSTALL.md +39 -0
- data/LICENSE +21 -0
- data/README.md +118 -0
- data/assets/screenshot.png +0 -0
- data/bin/wx-shapes +9 -0
- data/lib/wx/shapes/arrow_base.rb +86 -0
- data/lib/wx/shapes/arrows/circle_arrow.rb +39 -0
- data/lib/wx/shapes/arrows/diamond_arrow.rb +33 -0
- data/lib/wx/shapes/arrows/open_arrow.rb +56 -0
- data/lib/wx/shapes/arrows/solid_arrow.rb +69 -0
- data/lib/wx/shapes/art/shape_canvas/page.xpm +73 -0
- data/lib/wx/shapes/auto_layout.rb +358 -0
- data/lib/wx/shapes/base.rb +33 -0
- data/lib/wx/shapes/canvas_history.rb +84 -0
- data/lib/wx/shapes/connection_point.rb +238 -0
- data/lib/wx/shapes/core.rb +19 -0
- data/lib/wx/shapes/diagram.rb +659 -0
- data/lib/wx/shapes/events.rb +389 -0
- data/lib/wx/shapes/printout.rb +136 -0
- data/lib/wx/shapes/serializable.rb +440 -0
- data/lib/wx/shapes/serialize/core.rb +40 -0
- data/lib/wx/shapes/serialize/id.rb +82 -0
- data/lib/wx/shapes/serialize/wx.rb +104 -0
- data/lib/wx/shapes/serializer/json.rb +258 -0
- data/lib/wx/shapes/serializer/yaml.rb +125 -0
- data/lib/wx/shapes/shape.rb +2129 -0
- data/lib/wx/shapes/shape_canvas.rb +3285 -0
- data/lib/wx/shapes/shape_data_object.rb +43 -0
- data/lib/wx/shapes/shape_handle.rb +287 -0
- data/lib/wx/shapes/shape_list.rb +161 -0
- data/lib/wx/shapes/shapes/bitmap_shape.rb +257 -0
- data/lib/wx/shapes/shapes/circle_shape.rb +136 -0
- data/lib/wx/shapes/shapes/control_shape.rb +483 -0
- data/lib/wx/shapes/shapes/curve_shape.rb +231 -0
- data/lib/wx/shapes/shapes/diamond_shape.rb +62 -0
- data/lib/wx/shapes/shapes/edit_text_shape.rb +317 -0
- data/lib/wx/shapes/shapes/ellipse_shape.rb +106 -0
- data/lib/wx/shapes/shapes/flex_grid_shape.rb +78 -0
- data/lib/wx/shapes/shapes/grid_shape.rb +404 -0
- data/lib/wx/shapes/shapes/line_shape.rb +907 -0
- data/lib/wx/shapes/shapes/multi_sel_rect.rb +214 -0
- data/lib/wx/shapes/shapes/ortho_shape.rb +357 -0
- data/lib/wx/shapes/shapes/polygon_shape.rb +294 -0
- data/lib/wx/shapes/shapes/rect_shape.rb +378 -0
- data/lib/wx/shapes/shapes/round_ortho_shape.rb +131 -0
- data/lib/wx/shapes/shapes/round_rect_shape.rb +142 -0
- data/lib/wx/shapes/shapes/square_shape.rb +119 -0
- data/lib/wx/shapes/shapes/text_shape.rb +324 -0
- data/lib/wx/shapes/thumbnail.rb +234 -0
- data/lib/wx/shapes/version.rb +12 -0
- data/lib/wx/shapes/wx.rb +29 -0
- data/lib/wx/shapes.rb +18 -0
- data/lib/wx/wx-shapes/base.rb +87 -0
- data/lib/wx/wx-shapes/cmd/sampler.rb +58 -0
- data/lib/wx/wx-shapes/cmd/test.rb +27 -0
- data/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css +7 -0
- data/rakelib/yard/templates/default/layout/html/setup.rb +5 -0
- data/rakelib/yard/yard/relative_markdown_links/version.rb +8 -0
- data/rakelib/yard/yard/relative_markdown_links.rb +39 -0
- data/rakelib/yard/yard-custom-templates.rb +2 -0
- data/rakelib/yard/yard-relative_markdown_links.rb +4 -0
- data/samples/demo/art/AlignBottom.xpm +35 -0
- data/samples/demo/art/AlignCenter.xpm +35 -0
- data/samples/demo/art/AlignLeft.xpm +35 -0
- data/samples/demo/art/AlignMiddle.xpm +35 -0
- data/samples/demo/art/AlignRight.xpm +35 -0
- data/samples/demo/art/AlignTop.xpm +35 -0
- data/samples/demo/art/Bitmap.xpm +25 -0
- data/samples/demo/art/Circle.xpm +22 -0
- data/samples/demo/art/Curve.xpm +21 -0
- data/samples/demo/art/Diamond.xpm +22 -0
- data/samples/demo/art/EditText.xpm +21 -0
- data/samples/demo/art/Ellipse.xpm +22 -0
- data/samples/demo/art/FixedRect.xpm +22 -0
- data/samples/demo/art/FlexGrid.xpm +22 -0
- data/samples/demo/art/GC.xpm +23 -0
- data/samples/demo/art/Grid.xpm +22 -0
- data/samples/demo/art/Line.xpm +21 -0
- data/samples/demo/art/NoSource.xpm +69 -0
- data/samples/demo/art/OrthoLine.xpm +21 -0
- data/samples/demo/art/Rect.xpm +22 -0
- data/samples/demo/art/RoundOrthoLine.xpm +21 -0
- data/samples/demo/art/RoundRect.xpm +22 -0
- data/samples/demo/art/Shadow.xpm +23 -0
- data/samples/demo/art/StandAloneLine.xpm +22 -0
- data/samples/demo/art/Text.xpm +21 -0
- data/samples/demo/art/Tool.xpm +23 -0
- data/samples/demo/art/sample.xpm +251 -0
- data/samples/demo/demo.rb +658 -0
- data/samples/demo/frame_canvas.rb +422 -0
- data/samples/demo/images/motyl.bmp +0 -0
- data/samples/demo/images/motyl2.bmp +0 -0
- data/samples/sample1/art/sample.xpm +251 -0
- data/samples/sample1/sample.rb +263 -0
- data/samples/sample2/art/sample.xpm +251 -0
- data/samples/sample2/sample.rb +133 -0
- data/samples/sample2/sample_canvas.rb +35 -0
- data/samples/sample2/sample_shape.rb +108 -0
- data/samples/sample3/art/sample.xpm +251 -0
- data/samples/sample3/sample.rb +281 -0
- data/samples/sample4/art/sample.xpm +251 -0
- data/samples/sample4/sample.rb +180 -0
- data/tests/art/motyl.bmp +0 -0
- data/tests/lib/wxapp_runner.rb +64 -0
- data/tests/serializer_tests.rb +521 -0
- data/tests/test_grid_shapes.rb +42 -0
- data/tests/test_serialize.rb +7 -0
- data/tests/test_serialize_yaml.rb +17 -0
- metadata +242 -0
@@ -0,0 +1,440 @@
|
|
1
|
+
# Wx::SF::Serializer - shape serializer module
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module Wx::SF
|
7
|
+
|
8
|
+
module Serializable
|
9
|
+
|
10
|
+
class Property
|
11
|
+
def initialize(klass, prop, proc=nil, force: false, handler: nil, &block)
|
12
|
+
::Kernel.raise ArgumentError, "Invalid property id [#{prop}]" unless ::String === prop || ::Symbol === prop
|
13
|
+
::Kernel.raise ArgumentError, "Duplicate property id [#{prop}]" if klass.has_serializer_property?(prop)
|
14
|
+
@klass = klass
|
15
|
+
@id = prop.to_sym
|
16
|
+
@forced = force
|
17
|
+
if block || handler
|
18
|
+
if handler
|
19
|
+
::Kernel.raise ArgumentError,
|
20
|
+
"Invalid property handler #{handler} for #{prop}" unless ::Proc === handler || ::Symbol === handler
|
21
|
+
if handler.is_a?(::Proc)
|
22
|
+
::Kernel.raise ArgumentError, "Invalid property block #{proc} for #{prop}" unless block.arity == -3
|
23
|
+
@getter = ->(obj) { handler.call(@id, obj) }
|
24
|
+
@setter = ->(obj, val) { handler.call(@id, obj, val) }
|
25
|
+
else
|
26
|
+
@getter = ->(obj) { obj.send(handler, @id) }
|
27
|
+
@setter = ->(obj, val) { obj.send(handler, @id, val) }
|
28
|
+
end
|
29
|
+
else
|
30
|
+
# any property block MUST accept 2 or 3 args; property name, instance and value (for setter)
|
31
|
+
::Kernel.raise ArgumentError, "Invalid property block #{proc} for #{prop}" unless block.arity == -3
|
32
|
+
@getter = ->(obj) { block.call(@id, obj) }
|
33
|
+
@setter = ->(obj, val) { block.call(@id, obj, val) }
|
34
|
+
end
|
35
|
+
elsif proc
|
36
|
+
::Kernel.raise ArgumentError,
|
37
|
+
"Invalid property proc #{proc} for #{prop}" unless ::Proc === proc || ::Symbol === proc
|
38
|
+
if ::Proc === proc
|
39
|
+
# any property proc should be callable with a single arg (instance)
|
40
|
+
@getter = proc
|
41
|
+
# a property proc combining getter/setter functionality should accept a single or more args (instance + value)
|
42
|
+
@setter = (proc.arity == -2) ? proc : nil
|
43
|
+
else
|
44
|
+
@getter = ->(obj) { obj.send(proc) }
|
45
|
+
@setter = ->(obj, val) { obj.send(proc, val) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_reader :id
|
51
|
+
|
52
|
+
def serialize(obj, data, excludes)
|
53
|
+
unless excludes.include?(@id)
|
54
|
+
val = getter.call(obj)
|
55
|
+
unless Serializable === val && val.serialize_disabled? && !@forced
|
56
|
+
data[@id] = case val
|
57
|
+
when ::Array
|
58
|
+
val.select { |elem| !(Serializable === elem && elem.serialize_disabled?) }
|
59
|
+
when ::Set
|
60
|
+
::Set.new(val.select { |elem| !(Serializable === elem && elem.serialize_disabled?) })
|
61
|
+
else
|
62
|
+
val
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def deserialize(obj, data)
|
69
|
+
if data.has_key?(@id)
|
70
|
+
setter.call(obj, data[@id])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def get(obj)
|
75
|
+
getter.call(obj)
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_method(id)
|
79
|
+
begin
|
80
|
+
@klass.instance_method(id)
|
81
|
+
rescue NameError
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
private :get_method
|
86
|
+
|
87
|
+
def getter
|
88
|
+
unless @getter
|
89
|
+
inst_meth = get_method(@id)
|
90
|
+
inst_meth = get_method("get_#{@id}") unless inst_meth
|
91
|
+
if inst_meth
|
92
|
+
@getter = ->(obj) { inst_meth.bind(obj).call }
|
93
|
+
else
|
94
|
+
return self.method(:getter_fail)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
@getter
|
98
|
+
end
|
99
|
+
private :getter
|
100
|
+
|
101
|
+
def setter
|
102
|
+
unless @setter
|
103
|
+
inst_meth = get_method("#{@id}=")
|
104
|
+
inst_meth = get_method("set_#{@id}") unless inst_meth
|
105
|
+
unless inst_meth
|
106
|
+
im = get_method(@id)
|
107
|
+
if im && im.arity == -1
|
108
|
+
inst_meth = im
|
109
|
+
else
|
110
|
+
inst_meth = nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
if inst_meth
|
114
|
+
@setter = ->(obj, val) { inst_meth.bind(obj).call(val) }
|
115
|
+
else
|
116
|
+
return self.method(:setter_noop)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
@setter
|
120
|
+
end
|
121
|
+
private :setter
|
122
|
+
|
123
|
+
def getter_fail(obj)
|
124
|
+
::Kernel.raise RuntimeError, "Missing getter for property #{@id} of #{@klass}"
|
125
|
+
end
|
126
|
+
private :getter_fail
|
127
|
+
|
128
|
+
def setter_noop(_, _)
|
129
|
+
# do nothing
|
130
|
+
end
|
131
|
+
private :setter_noop
|
132
|
+
end
|
133
|
+
|
134
|
+
# Serializable unique ids.
|
135
|
+
# This class makes sure to maintain uniqueness across serialization/deserialization cycles
|
136
|
+
# and keeps all shared instances within a single (serialized/deserialized) object set in
|
137
|
+
# sync.
|
138
|
+
class ID; end
|
139
|
+
|
140
|
+
class << self
|
141
|
+
|
142
|
+
def serializables
|
143
|
+
@serializables ||= ::Set.new
|
144
|
+
end
|
145
|
+
|
146
|
+
def formatters
|
147
|
+
@formatters ||= {}
|
148
|
+
end
|
149
|
+
private :formatters
|
150
|
+
|
151
|
+
# Registers a serialization formatting engine
|
152
|
+
# @param [Symbol,String] format format id
|
153
|
+
# @param [Object] engine formatting engine
|
154
|
+
def register(format, engine)
|
155
|
+
if formatters.has_key?(format.to_s.downcase)
|
156
|
+
::Kernel.raise ArgumentError,
|
157
|
+
"Duplicate serialization formatter registration for #{format}"
|
158
|
+
end
|
159
|
+
formatters[format.to_s.downcase] = engine
|
160
|
+
end
|
161
|
+
|
162
|
+
# Return a serialization formatting engine
|
163
|
+
# @param [Symbol,String] format format id
|
164
|
+
# @return [Object] formatting engine
|
165
|
+
def [](format)
|
166
|
+
formatters[format.to_s.downcase]
|
167
|
+
end
|
168
|
+
|
169
|
+
def default_format
|
170
|
+
@default_format ||= :json
|
171
|
+
end
|
172
|
+
|
173
|
+
def default_format=(format)
|
174
|
+
@default_format = format
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
# Mixin module for classes that get Wx::SF::Serializable included.
|
180
|
+
# This module is used to extend the class methods of the serializable class.
|
181
|
+
module SerializeClassMethods
|
182
|
+
|
183
|
+
# Adds (a) serializable property(-ies) for instances of his class (and derived classes)
|
184
|
+
# @overload property(*props, force: false)
|
185
|
+
# Specifies one or more serialized properties.
|
186
|
+
# The serialization framework will determine the availability of setter and getter methods
|
187
|
+
# automatically by looking for methods <code>"#{prop_id}=(v)"</code>, <code>"set_#{prop_id}(v)"</code> or <code>"#{prop}(v)"</code>
|
188
|
+
# for setters and <code>"#{prop_id}()"</code> or <code>"get_#{prop_id}"</code> for getters.
|
189
|
+
# @param [Symbol,String] props one or more ids of serializable properties
|
190
|
+
# @param [Boolean] force overrides any #disable_serialize for the properties specified
|
191
|
+
# @overload property(hash, force: false)
|
192
|
+
# Specifies one or more serialized properties with associated setter/getter method ids/procs/lambda-s.
|
193
|
+
# @example
|
194
|
+
# property(
|
195
|
+
# prop_a: ->(obj, *val) {
|
196
|
+
# obj.my_prop_a_setter(val.first) unless val.empty?
|
197
|
+
# obj.my_prop_a_getter
|
198
|
+
# },
|
199
|
+
# prop_b: Proc.new { |obj, *val|
|
200
|
+
# obj.my_prop_b_setter(val.first) unless val.empty?
|
201
|
+
# obj.my_prop_b_getter
|
202
|
+
# },
|
203
|
+
# prop_c: :serialization_method)
|
204
|
+
# Procs with setter support MUST accept 1 or 2 arguments (1 for getter, 2 for setter).
|
205
|
+
# @note Use `*val` to specify the optional value argument for setter requests instead of `val=nil`
|
206
|
+
# to be able to support setting explicit nil values.
|
207
|
+
# @param [Hash] hash a hash of pairs of property ids and getter/setter procs
|
208
|
+
# @param [Boolean] force overrides any #disable_serialize for the properties specified
|
209
|
+
# @overload property(*props, force: false, handler: nil, &block)
|
210
|
+
# Specifies one or more serialized properties with a getter/setter handler proc/method/block.
|
211
|
+
# The getter/setter proc or block should accept either 2 (property id and object for getter) or 3 arguments
|
212
|
+
# (property id, object and value for setter) and is assumed to handle getter/setter requests
|
213
|
+
# for all specified properties.
|
214
|
+
# The getter/setter method should accept either 1 (property id for getter) or 2 arguments
|
215
|
+
# (property id and value for setter) and is assumed to handle getter/setter requests
|
216
|
+
# for all specified properties.
|
217
|
+
# @example
|
218
|
+
# property(:property_a, :property_b, :property_c) do |id, obj, *val|
|
219
|
+
# case id
|
220
|
+
# when :property_a
|
221
|
+
# ...
|
222
|
+
# when :property_b
|
223
|
+
# ...
|
224
|
+
# when :property_c
|
225
|
+
# ...
|
226
|
+
# end
|
227
|
+
# end
|
228
|
+
# @note Use `*val` to specify the optional value argument for setter requests instead of `val=nil`
|
229
|
+
# to be able to support setting explicit nil values.
|
230
|
+
# @param [Symbol,String] props one or more ids of serializable properties
|
231
|
+
# @param [Boolean] force overrides any #disable_serialize for the properties specified
|
232
|
+
# @yieldparam [Symbol,String] id property id
|
233
|
+
# @yieldparam [Object] obj object instance
|
234
|
+
# @yieldparam [Object] val optional property value to set in case of setter request
|
235
|
+
def property(*props, **kwargs, &block)
|
236
|
+
forced = !!kwargs.delete(:force)
|
237
|
+
if block || kwargs[:handler]
|
238
|
+
props.each do |prop|
|
239
|
+
serializer_properties << Property.new(self, prop, force: forced, handler: kwargs[:handler], &block)
|
240
|
+
end
|
241
|
+
else
|
242
|
+
props.flatten.each do |prop|
|
243
|
+
if ::Hash === prop
|
244
|
+
prop.each_pair do |pn, pp|
|
245
|
+
serializer_properties << Property.new(self, pn, pp, force: forced)
|
246
|
+
end
|
247
|
+
else
|
248
|
+
serializer_properties << Property.new(self, prop, force: forced)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
unless kwargs.empty?
|
252
|
+
kwargs.each_pair do |pn, pp|
|
253
|
+
serializer_properties << Property.new(self, pn, pp, force: forced)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
alias :properties :property
|
259
|
+
alias :contains :property
|
260
|
+
|
261
|
+
# excludes a serializable property for instances of this class
|
262
|
+
# (mostly/only useful to exclude properties from base classes which
|
263
|
+
# do not require serialization for derived class)
|
264
|
+
def excluded_property(*props)
|
265
|
+
excluded_serializer_properties.merge props.flatten.collect { |prop| prop.to_s }
|
266
|
+
end
|
267
|
+
alias :excluded_properties :excluded_property
|
268
|
+
alias :excludes :excluded_property
|
269
|
+
|
270
|
+
# Creates a new instance for subsequent deserialization and optionally initialize
|
271
|
+
# it using the given data hash.
|
272
|
+
# The default implementation creates a new instance using the default constructor
|
273
|
+
# (no arguments, no initialization) and leaves the initialization to a subsequent call
|
274
|
+
# to the instance method #from_serialized(data).
|
275
|
+
# Classes that do not support a default constructor can override this class method and
|
276
|
+
# implement a custom creation scheme.
|
277
|
+
# @param [Hash] _data hash containing deserialized property data (symbol keys)
|
278
|
+
# @return [Object] the newly created object
|
279
|
+
def create_for_deserialize(_data)
|
280
|
+
self.new
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
# Mixin module for classes that get Wx::SF::Serializable included.
|
286
|
+
# This module is used to extend the instance methods of the serializable class.
|
287
|
+
module SerializeInstanceMethods
|
288
|
+
|
289
|
+
# Serialize this object
|
290
|
+
# @overload serialize(pretty: false, format: Serializable.default_format)
|
291
|
+
# @param [Boolean] pretty if true specifies to generate pretty formatted output if possible
|
292
|
+
# @param [Symbol,String] format specifies output format
|
293
|
+
# @return [String] serialized data
|
294
|
+
# @overload serialize(io, pretty: false, format: Serializable.default_format)
|
295
|
+
# @param [IO] io output stream to write serialized data to
|
296
|
+
# @param [Boolean] pretty if true specifies to generate pretty formatted output if possible
|
297
|
+
# @param [Symbol,String] format specifies output format
|
298
|
+
# @return [IO]
|
299
|
+
def serialize(io = nil, pretty: false, format: Serializable.default_format)
|
300
|
+
Serializable[format].dump(self, io, pretty: pretty)
|
301
|
+
end
|
302
|
+
|
303
|
+
# Returns true if regular serialization for this object has been disabled, false otherwise (default).
|
304
|
+
# Disabled serialization can be overridden for single objects (not objects maintained in property containers
|
305
|
+
# like arrays and sets).
|
306
|
+
# @return [Boolean]
|
307
|
+
def serialize_disabled?
|
308
|
+
!!@serialize_disabled # true for any value but false
|
309
|
+
end
|
310
|
+
|
311
|
+
# Disables serialization for this object as a single property or as part of a property container
|
312
|
+
# (array or set).
|
313
|
+
# @return [void]
|
314
|
+
def disable_serialize
|
315
|
+
# by default unset (nil) so serializing enabled
|
316
|
+
@serialize_disabled = true
|
317
|
+
end
|
318
|
+
|
319
|
+
# @!method for_serialize(hash, excludes = Set.new)
|
320
|
+
# Serializes the properties of a serializable instance to the given hash
|
321
|
+
# except when the property id is included in excludes.
|
322
|
+
# @param [Hash] hash property serialization hash
|
323
|
+
# @param [Set] excludes set with excluded property ids
|
324
|
+
# @return [Hash] property serialization hash
|
325
|
+
|
326
|
+
# @!method from_serialized(hash)
|
327
|
+
# Restores the properties of a deserialized instance.
|
328
|
+
# @param [Hash] hash deserialized properties hash
|
329
|
+
# @return [self]
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
# Serialize the given object
|
334
|
+
# @overload serialize(obj, pretty: false, format: Serializable.default_format)
|
335
|
+
# @param [Object] obj object to serialize
|
336
|
+
# @param [Boolean] pretty if true specifies to generate pretty formatted output if possible
|
337
|
+
# @param [Symbol,String] format specifies output format
|
338
|
+
# @return [String] serialized data
|
339
|
+
# @overload serialize(obj, io, pretty: false, format: Serializable.default_format)
|
340
|
+
# @param [Object] obj object to serialize
|
341
|
+
# @param [IO] io output stream to write serialized data to
|
342
|
+
# @param [Boolean] pretty if true specifies to generate pretty formatted output if possible
|
343
|
+
# @param [Symbol,String] format specifies output format
|
344
|
+
# @return [IO]
|
345
|
+
def self.serialize(obj, io = nil, pretty: false, format: Serializable.default_format)
|
346
|
+
self[format].dump(obj, io, pretty: pretty)
|
347
|
+
end
|
348
|
+
|
349
|
+
# Deserializes object from source data
|
350
|
+
# @param [IO,String] source source data (stream)
|
351
|
+
# @param [Symbol, String] format data format of source
|
352
|
+
# @return [Object] deserialized object
|
353
|
+
def self.deserialize(source, format: Serializable.default_format)
|
354
|
+
self[format].load(::IO === source || source.respond_to?(:read) ? source.read : source)
|
355
|
+
end
|
356
|
+
|
357
|
+
def self.included(base)
|
358
|
+
::Kernel.raise RuntimeError, "#{self} should only be included in classes" if base.instance_of?(::Module)
|
359
|
+
|
360
|
+
# register as serializable class
|
361
|
+
Serializable.serializables << base
|
362
|
+
|
363
|
+
return if base == Serializable::ID # special case which does not need the rest
|
364
|
+
|
365
|
+
# provide serialized property definition support
|
366
|
+
|
367
|
+
# provide serialized classes with their own serialized properties (exclusion) list
|
368
|
+
base.singleton_class.class_eval do
|
369
|
+
def serializer_properties
|
370
|
+
@serializer_props ||= []
|
371
|
+
end
|
372
|
+
def excluded_serializer_properties
|
373
|
+
@excluded_serializer_props ||= ::Set.new
|
374
|
+
end
|
375
|
+
end
|
376
|
+
# add class methods
|
377
|
+
base.extend(SerializeClassMethods)
|
378
|
+
|
379
|
+
# add instance property (de-)serialization methods for base class
|
380
|
+
base.class_eval <<~__CODE
|
381
|
+
def for_serialize(hash, excludes = ::Set.new)
|
382
|
+
hash[:'@explicit'] = true if serialize_disabled? # mark explicit serialize overriding disabling
|
383
|
+
#{base.name}.serializer_properties.each { |prop, h| prop.serialize(self, hash, excludes) }
|
384
|
+
hash
|
385
|
+
end
|
386
|
+
protected :for_serialize
|
387
|
+
|
388
|
+
def from_serialized(hash)
|
389
|
+
disable_serialize if hash[:'@explicit'] # re-instate serialization disabling
|
390
|
+
#{base.name}.serializer_properties.each { |prop| prop.deserialize(self, hash) }
|
391
|
+
self
|
392
|
+
end
|
393
|
+
protected :from_serialized
|
394
|
+
|
395
|
+
def self.has_serializer_property?(id)
|
396
|
+
self.serializer_properties.any? { |p| p.id == id.to_sym }
|
397
|
+
end
|
398
|
+
__CODE
|
399
|
+
# add inheritance support
|
400
|
+
base.class_eval do
|
401
|
+
def self.inherited(derived)
|
402
|
+
# add instance property (de-)serialization methods for derived classes
|
403
|
+
derived.class_eval <<~__CODE
|
404
|
+
module SerializerMethods
|
405
|
+
def for_serialize(hash, excludes = ::Set.new)
|
406
|
+
hash = super(hash, excludes | #{derived.name}.excluded_serializer_properties)
|
407
|
+
#{derived.name}.serializer_properties.each { |prop| prop.serialize(self, hash, excludes) }
|
408
|
+
hash
|
409
|
+
end
|
410
|
+
protected :for_serialize
|
411
|
+
|
412
|
+
def from_serialized(hash)
|
413
|
+
#{derived.name}.serializer_properties.each { |prop| prop.deserialize(self, hash) }
|
414
|
+
super(hash)
|
415
|
+
end
|
416
|
+
protected :from_serialized
|
417
|
+
end
|
418
|
+
include SerializerMethods
|
419
|
+
__CODE
|
420
|
+
derived.class_eval do
|
421
|
+
def self.has_serializer_property?(id)
|
422
|
+
self.serializer_properties.any? { |p| p.id == id.to_sym } || self.superclass.has_serializer_property?(id)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# register as serializable class
|
427
|
+
Serializable.serializables << derived
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# add instance serialization method
|
432
|
+
base.include(SerializeInstanceMethods)
|
433
|
+
end
|
434
|
+
|
435
|
+
end # module Serializable
|
436
|
+
|
437
|
+
end # module Wx::SF
|
438
|
+
|
439
|
+
Dir[File.join(__dir__, 'serializer', '*.rb')].each { |fnm| require "wx/shapes/serializer/#{File.basename(fnm)}" }
|
440
|
+
Dir[File.join(__dir__, 'serialize', '*.rb')].each { |fnm| require "wx/shapes/serialize/#{File.basename(fnm)}" }
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Wx::SF::Serializer - Ruby core serializer extensions
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
# we do not include Wx::SF::Serializer::SerializeMethod here as that would
|
5
|
+
# also extend these classes with the engine specific extension that we do not
|
6
|
+
# need or want here
|
7
|
+
|
8
|
+
class Array
|
9
|
+
def serialize(io = nil, pretty: false, format: Wx::SF::Serializable.default_format)
|
10
|
+
Wx::SF::Serializable[format].dump(self, io, pretty: pretty)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Hash
|
15
|
+
def serialize(io = nil, pretty: false, format: Wx::SF::Serializable.default_format)
|
16
|
+
Wx::SF::Serializable[format].dump(self, io, pretty: pretty)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Struct
|
21
|
+
def serialize(io = nil, pretty: false, format: Wx::SF::Serializable.default_format)
|
22
|
+
Wx::SF::Serializable[format].dump(self, io, pretty: pretty)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'set'
|
27
|
+
|
28
|
+
class Set
|
29
|
+
def serialize(io = nil, pretty: false, format: Wx::SF::Serializable.default_format)
|
30
|
+
Wx::SF::Serializable[format].dump(self, io, pretty: pretty)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'ostruct'
|
35
|
+
|
36
|
+
class OpenStruct
|
37
|
+
def serialize(io = nil, pretty: false, format: Wx::SF::Serializable.default_format)
|
38
|
+
Wx::SF::Serializable[format].dump(self, io, pretty: pretty)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Wx::SF::Serializer - Wx::SF serializable ID class
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
module Wx::SF::Serializable
|
5
|
+
|
6
|
+
class ID
|
7
|
+
|
8
|
+
include Wx::SF::Serializable
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
TLS_RESTORATION_MAP_KEY = :wx_sf_serializable_id_restoration_map.freeze
|
13
|
+
private_constant :TLS_RESTORATION_MAP_KEY
|
14
|
+
|
15
|
+
def init_restoration_map
|
16
|
+
(::Thread.current[TLS_RESTORATION_MAP_KEY] ||= []) << {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def clear_restoration_map
|
20
|
+
::Thread.current[TLS_RESTORATION_MAP_KEY].pop
|
21
|
+
end
|
22
|
+
|
23
|
+
def restoration_map
|
24
|
+
::Thread.current[TLS_RESTORATION_MAP_KEY].last
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a Serialized::Id instance matching the deserialized id number
|
30
|
+
# either by retrieving an earlier restored Id from the (thread/fiber-)current
|
31
|
+
# restoration map or creating (and mapping) a new Id instance.
|
32
|
+
# @param [Hash] data deserialized properties hash
|
33
|
+
# @return [ID] restored ID instance
|
34
|
+
# @see SerializeClassMethods#create_for_deserialize
|
35
|
+
def self.create_for_deserialize(data)
|
36
|
+
serialized_id = data[:id] || 0
|
37
|
+
restoration_map[serialized_id] ||= self.new
|
38
|
+
end
|
39
|
+
|
40
|
+
# Collects the ID's object_id for serialization.
|
41
|
+
# Note that this is fixed and cannot be excluded.
|
42
|
+
# @param [Hash] hash property serialization hash
|
43
|
+
# @param [Set] _excludes ignored
|
44
|
+
# @return [Hash] property serialization hash
|
45
|
+
def for_serialize(hash, _excludes=nil)
|
46
|
+
hash[:id] = self.object_id
|
47
|
+
hash
|
48
|
+
end
|
49
|
+
protected :for_serialize
|
50
|
+
|
51
|
+
# Noop for ID instances.
|
52
|
+
# @param [Hash] _hash ignored
|
53
|
+
# @return [self]
|
54
|
+
def from_serialized(_hash)
|
55
|
+
# no deserializing necessary
|
56
|
+
self
|
57
|
+
end
|
58
|
+
protected :from_serialized
|
59
|
+
|
60
|
+
# Always returns false for IDs.
|
61
|
+
# @return [Boolean]
|
62
|
+
def serialize_disabled?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
"Wx::SF::Serializable::ID<#{object_id}>"
|
68
|
+
end
|
69
|
+
|
70
|
+
def inspect
|
71
|
+
to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_i
|
75
|
+
object_id
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
serializables << ID
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Wx::SF::Serializer - Wx class serializer extensions
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
class Wx::Point
|
5
|
+
|
6
|
+
include Wx::SF::Serializable
|
7
|
+
|
8
|
+
properties :x, :y
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
class Wx::RealPoint
|
13
|
+
|
14
|
+
include Wx::SF::Serializable
|
15
|
+
|
16
|
+
properties :x, :y
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class Wx::Size
|
21
|
+
|
22
|
+
include Wx::SF::Serializable
|
23
|
+
|
24
|
+
properties :width, :height
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class Wx::Rect
|
29
|
+
|
30
|
+
include Wx::SF::Serializable
|
31
|
+
|
32
|
+
properties :x, :y, :width, :height
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class Wx::Enum
|
37
|
+
|
38
|
+
include Wx::SF::Serializable
|
39
|
+
|
40
|
+
property :value => ->(enum) { enum.to_i }
|
41
|
+
|
42
|
+
def self.create_for_deserialize(data)
|
43
|
+
self.new(data[:value] || 0)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
class Wx::Colour
|
49
|
+
|
50
|
+
include Wx::SF::Serializable
|
51
|
+
|
52
|
+
property :colour => ->(col, *val) { col.set(*val.first) unless val.empty?; [col.red, col.green, col.blue, col.alpha] }
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
# need to add this Enum explicitly as it was initially defined before we extended the Wx::Enum class above
|
57
|
+
class Wx::BrushStyle
|
58
|
+
|
59
|
+
property :value => ->(enum) { enum.to_i }
|
60
|
+
|
61
|
+
include Wx::SF::Serializable
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
class Wx::Brush
|
66
|
+
|
67
|
+
include Wx::SF::Serializable
|
68
|
+
|
69
|
+
property :colour, :style
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# need to add this Enum explicitly as it was initially defined before we extended the Wx::Enum class above
|
74
|
+
class Wx::PenStyle
|
75
|
+
|
76
|
+
property :value => ->(enum) { enum.to_i }
|
77
|
+
|
78
|
+
include Wx::SF::Serializable
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
class Wx::Pen
|
83
|
+
|
84
|
+
include Wx::SF::Serializable
|
85
|
+
|
86
|
+
property :colour, :width, :style
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
class Wx::Font
|
91
|
+
|
92
|
+
include Wx::SF::Serializable
|
93
|
+
|
94
|
+
property font_info: ->(font, *info) { font.set_native_font_info_user_desc(info.shift) unless info.empty?; font.get_native_font_info_user_desc }
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
class Wx::BitmapType
|
99
|
+
|
100
|
+
property :value => ->(enum) { enum.to_i }
|
101
|
+
|
102
|
+
include Wx::SF::Serializable
|
103
|
+
|
104
|
+
end
|