foxguib 1.1.0

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 (109) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/changelog.txt +128 -0
  4. data/doc/www/index.html +173 -0
  5. data/doc/www/upload.rb +9 -0
  6. data/examples/event_handler.rb +67 -0
  7. data/examples/event_handlers.rbin +0 -0
  8. data/examples/guib_testdialog.rb +31 -0
  9. data/examples/testdialog.rb +29 -0
  10. data/exe/foxguib +1 -0
  11. data/foxGUIb-debug.rb +37 -0
  12. data/foxGUIb.rb +88 -0
  13. data/foxGUIb_win32.rbw +1 -0
  14. data/foxguib.gemspec +26 -0
  15. data/license.txt +49 -0
  16. data/readme.txt +1 -0
  17. data/release_notes.txt +68 -0
  18. data/src/assert.rb +8 -0
  19. data/src/cfgman.rb +27 -0
  20. data/src/code-gen/code-generator.rb +171 -0
  21. data/src/code-gen/property-generators.rb +65 -0
  22. data/src/code-gen/widget-generator.rb +22 -0
  23. data/src/convert.rb +60 -0
  24. data/src/direct_widget_manipulation.rb +343 -0
  25. data/src/docman.rb +260 -0
  26. data/src/document.rb +405 -0
  27. data/src/event_categories.yaml +85 -0
  28. data/src/event_listener.rb +57 -0
  29. data/src/events.yaml +77 -0
  30. data/src/events_docu.yaml +228 -0
  31. data/src/get_events.rb +64 -0
  32. data/src/gui/ArrowButton.tga +0 -0
  33. data/src/gui/BoxMinus.tga +0 -0
  34. data/src/gui/BoxPlus.tga +0 -0
  35. data/src/gui/Button.tga +0 -0
  36. data/src/gui/CheckButton.tga +0 -0
  37. data/src/gui/ComboBox.tga +0 -0
  38. data/src/gui/Dial.tga +0 -0
  39. data/src/gui/GroupBox.tga +0 -0
  40. data/src/gui/HorizontalFrame.tga +0 -0
  41. data/src/gui/HorizontalSeparator.tga +0 -0
  42. data/src/gui/Label.tga +0 -0
  43. data/src/gui/MDIChild.tga +0 -0
  44. data/src/gui/MDIClient.tga +0 -0
  45. data/src/gui/Matrix.tga +0 -0
  46. data/src/gui/MenuCaption.tga +0 -0
  47. data/src/gui/MenuCascade.tga +0 -0
  48. data/src/gui/MenuCheck.tga +0 -0
  49. data/src/gui/MenuCommand.tga +0 -0
  50. data/src/gui/MenuPane.tga +0 -0
  51. data/src/gui/MenuRadio.tga +0 -0
  52. data/src/gui/MenuSeparator.tga +0 -0
  53. data/src/gui/MenuTitle.tga +0 -0
  54. data/src/gui/Menubar.tga +0 -0
  55. data/src/gui/Packer.tga +0 -0
  56. data/src/gui/ProgressBar.tga +0 -0
  57. data/src/gui/RadioButton.tga +0 -0
  58. data/src/gui/RadioMutex.tga +0 -0
  59. data/src/gui/ScrollWindow.tga +0 -0
  60. data/src/gui/Scrollbar.tga +0 -0
  61. data/src/gui/Shutter.tga +0 -0
  62. data/src/gui/ShutterItem.tga +0 -0
  63. data/src/gui/Slider.tga +0 -0
  64. data/src/gui/Spinner.tga +0 -0
  65. data/src/gui/Splitter.tga +0 -0
  66. data/src/gui/StatusBar.tga +0 -0
  67. data/src/gui/Statusline.tga +0 -0
  68. data/src/gui/Switcher.tga +0 -0
  69. data/src/gui/TabBar.tga +0 -0
  70. data/src/gui/TabBook.tga +0 -0
  71. data/src/gui/TabItem.tga +0 -0
  72. data/src/gui/Text.tga +0 -0
  73. data/src/gui/TextField.tga +0 -0
  74. data/src/gui/ToggleButton.tga +0 -0
  75. data/src/gui/Toolbar.tga +0 -0
  76. data/src/gui/VerticalFrame.tga +0 -0
  77. data/src/gui/VerticalSeparator.tga +0 -0
  78. data/src/gui/_guib_event_editor.rb +185 -0
  79. data/src/gui/_guib_event_editor.rbin +0 -0
  80. data/src/gui/_guib_genruby.rb +414 -0
  81. data/src/gui/_guib_genruby.rbin +0 -0
  82. data/src/gui/_guib_mainwin.rb +574 -0
  83. data/src/gui/_guib_mainwin.rbin +0 -0
  84. data/src/gui/event_editor.rb +180 -0
  85. data/src/gui/genruby.rb +155 -0
  86. data/src/gui/guib__console.rb +265 -0
  87. data/src/gui/mainwin.rb +71 -0
  88. data/src/gui/ruby_console.rb +263 -0
  89. data/src/gui/svg/Matrix.svg +87 -0
  90. data/src/gui/svg/MenuCheck.svg +135 -0
  91. data/src/gui/svg/MenuRadio.svg +130 -0
  92. data/src/gui/svg/RadioMutex.svg +119 -0
  93. data/src/gui/textview.rb +101 -0
  94. data/src/gui/textview.rbin +0 -0
  95. data/src/minitest.rb +60 -0
  96. data/src/prop-lists.rb +323 -0
  97. data/src/prop-types.rb +606 -0
  98. data/src/properties.rb +119 -0
  99. data/src/propman.rb +88 -0
  100. data/src/relink_mechanisms.rb +95 -0
  101. data/src/serialize.rb +167 -0
  102. data/src/state_machine.rb +73 -0
  103. data/src/version.rb +6 -0
  104. data/src/widget-lists.rb +118 -0
  105. data/src/widget-selector.rb +85 -0
  106. data/src/widgettree-controls.rb +183 -0
  107. data.tar.gz.sig +0 -0
  108. metadata +236 -0
  109. metadata.gz.sig +0 -0
data/src/document.rb ADDED
@@ -0,0 +1,405 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ require "assert"
4
+ require "code-gen/code-generator"
5
+ require "serialize"
6
+ require "widgettree-controls"
7
+ require "relink_mechanisms"
8
+
9
+ MARKED_COLOR = Fox::FXRGB(1, 150, 250)
10
+
11
+ class UserData # TODO: to be ELIMINATED
12
+ def initialize treeitem, manager
13
+ raise "treeitem is nil" unless treeitem
14
+ @treeitem = treeitem
15
+ @manager = manager
16
+ end
17
+ attr_accessor :treeitem, :manager
18
+ end
19
+
20
+ class Document
21
+ include CodeGenerator
22
+ include Serialize
23
+ include WidgettreeControls
24
+ def initialize etype
25
+ @switcherIndex = 0
26
+ create_dlg # @ WidgettreeControls.rb
27
+ @w = {}
28
+ @names = {}
29
+ @implicit_children = true
30
+ name = nil
31
+ @type = etype
32
+ init(etype) if etype
33
+ @gendlg_state = {}
34
+ end
35
+
36
+ attr_accessor :doclist_item
37
+ attr_accessor :name
38
+ attr_accessor :topwin, :type,
39
+ :dlg,
40
+ :marked,
41
+ :marked_wcolor,
42
+ :fxtree,
43
+ :switcherIndex, # the switcher-index of the treelist for this Document
44
+ :gendlg_state
45
+ # ~ attr_reader :w # breaks codegen
46
+
47
+ def init etype
48
+ @w[@root.to_s] = MAINWIN.topwin
49
+ classname = name = ""
50
+ if etype == "mainwin"
51
+ classname = "#{DialogBox}"
52
+ name = "mainWindow"
53
+ elsif etype == "dlg"
54
+ classname = "#{DialogBox}"
55
+ name = "dialogBox"
56
+ elsif etype == "popup"
57
+ # TODO:
58
+ end
59
+ if name
60
+ @topwin = add_widget classname, nil, @root
61
+ rename_wdg(@topwin, name)
62
+ @topwin.resize(500, 400)
63
+ @topwin.show(PLACEMENT_SCREEN)
64
+ end
65
+ if etype == "mainwin"
66
+ def @topwin.class
67
+ MainWindow
68
+ end
69
+ end
70
+ end
71
+
72
+ def load filename
73
+ data = load_from_file(filename)
74
+ @name = filename
75
+ @names = data[:names] # to avoid inconsistencies while loading the widgettree
76
+ @type = data[:type]
77
+ @gendlg_state = data[:gendlg_state]
78
+ init @type
79
+ rename_wdg(@topwin, data[:hierarchy].first[:name])
80
+ @topwin.set_name(data[:hierarchy].first[:name])
81
+ @topwin.deserialize(data[:hierarchy].first[ :prop])
82
+ load_widgettree data[:hierarchy].first[:sub], "inside", @topwin
83
+ @names = data[:names] # to reset to the saved state
84
+ @topwin.layout
85
+ @fxtree.currentItem = @topwin.userData.treeitem
86
+ @topwin.update_properties
87
+ unmark
88
+ mark @topwin
89
+ end
90
+
91
+ # recursive loading function of a subtree. used by "load" and "paste".
92
+ # hashes: an array of hashes representing widgets that shall be created where(=inside/after/before the relativeWidget)
93
+ def load_widgettree hashes, where, relativeWdg
94
+ tmp = @implicit_children
95
+ @implicit_children = false
96
+ hashes.each { |hash|
97
+ # hash[:name]=get_name(hash[:name]) if @w.has_key?( hash[:name])
98
+ wdg = add_widget(hash[:cl], where, relativeWdg.userData.treeitem)
99
+ unless wdg
100
+ puts "add_widget returned nil for <#{hash[:name]}>!"
101
+ next
102
+ end
103
+ wdg.deserialize hash[:prop]
104
+ wdg.behaviour = hash[:behaviour] if hash[:behaviour]
105
+ load_widgettree hash[:sub], "inside", wdg
106
+ rename_wdg(wdg, hash[:name])
107
+ }
108
+ @implicit_children = tmp
109
+ end
110
+
111
+ def save filename
112
+ unmark
113
+ data = {
114
+ type: @type,
115
+ names: @names,
116
+ hierarchy: [],
117
+ gendlg_state: @gendlg_state
118
+ }
119
+ start_node = @root.below
120
+ traverse(@topwin, data[:hierarchy])
121
+ if filename
122
+ $fxapp.beginWaitCursor
123
+ save_to_file filename, data
124
+ $fxapp.endWaitCursor
125
+ else
126
+ puts "no filename given. didn't save."
127
+ end
128
+ data
129
+ end
130
+
131
+ def traverse(widget, nodelist)
132
+ return unless widget
133
+ hash = {}
134
+ subnodes = []
135
+ hash[:sub] = subnodes
136
+ nodelist << hash
137
+ # wdg=@w[treenode.to_s]
138
+ hash[:name] = widget.wdg_name # wdg.userData.treeitem.text
139
+ hash[:cl] = widget.class.to_s.sub("FX::", "")
140
+ prophash = {}
141
+ hash[:prop] = prophash
142
+ widget.serialize prophash
143
+ hash[:behaviour] = widget.behaviour
144
+ widget.each_child { |child|
145
+ traverse(child, subnodes) if child.respond_to? :hilighted=
146
+ }
147
+ traverse(widget.menu, subnodes) if widget.respond_to? :menu
148
+ end
149
+
150
+ def add_widget classname, opts = "after", currentitem = @fxtree.currentItem
151
+ assert(currentitem)
152
+ wdg = parent = parentitem = nil
153
+ if currentitem.parent == @root and opts != "inside"
154
+ # linking as subnode
155
+ parent = @w[currentitem.to_s]
156
+ parentitem = currentitem
157
+ opts = "inside"
158
+ elsif opts == "after" or opts == "before"
159
+ parent = @w[currentitem.parent.to_s]
160
+ parentitem = currentitem.parent
161
+ siblingitem = currentitem
162
+ elsif opts == "inside"
163
+ parent = @w[currentitem.to_s]
164
+ parentitem = currentitem
165
+ elsif currentitem == @root
166
+ parent = @w[currentitem.to_s]
167
+ parentitem = currentitem
168
+ end
169
+ begin
170
+ wdg = create_wdg(classname, parent)
171
+ raise "error creating wdg" unless wdg
172
+ wdg.wdg_name = name = add_reference(classname.downcase, wdg)
173
+ currentitem = @fxtree.appendItem parentitem, name
174
+ wdg.userData = UserData.new(currentitem, self)
175
+ @fxtree.currentItem = currentitem
176
+ if opts == "after" or opts == "before"
177
+ currentitem = relink_wdg(currentitem, opts, siblingitem).userData.treeitem
178
+ end
179
+ create_implicit_children classname
180
+ expand_parents currentitem
181
+ @fxtree.forceRefresh
182
+ set_dirty
183
+ wdg.update_properties
184
+ rescue
185
+ puts $!
186
+ puts $!.backtrace.join("\n")
187
+ end
188
+ wdg
189
+ end
190
+
191
+ def create_wdg classname, p
192
+ wdg = nil
193
+ assert(p)
194
+ check_integrity classname, p
195
+ wdg = eval(classname).new p
196
+ m = wdg.method(:backColor)
197
+ # cache real backColor so that marked widgets don't get confused with their markedColor and real backColor
198
+ def wdg.backColor=c
199
+ @backColor = c
200
+ super unless @hilighted
201
+ end
202
+
203
+ def wdg.backColor
204
+ @backColor
205
+ end
206
+
207
+ def wdg.hilighted=b
208
+ @hilighted = b
209
+ c = @hilighted ? MARKED_COLOR : @backColor
210
+ setBackColor(c)
211
+ end
212
+ wdg.backColor = wdg.getBackColor
213
+ wdg.enable
214
+ wdg.connect(SEL_LEFTBUTTONPRESS) {
215
+ onClick wdg
216
+ 0
217
+ }
218
+ wdg.connect(SEL_RIGHTBUTTONPRESS) { |x, y, e|
219
+ unmark; # mark wdg
220
+ onClick wdg
221
+ @wdgMenu.popup_over(wdg, e.rootclick_x, e.rootclick_y)
222
+ 0
223
+ }
224
+ wdg.create
225
+ wdg.recalc
226
+ wdg.shell.forceRefresh
227
+ wdg.create_defaults
228
+ unmark
229
+ mark wdg
230
+ wdg
231
+ end
232
+
233
+ def create_implicit_children parentclass
234
+ return unless @implicit_children
235
+ if ["Menubar"].member? parentclass
236
+ add_widget "MenuTitle", "inside"
237
+ elsif ["MenuTitle", "MenuCascade", "OptionMenu", "MenuButton"].member? parentclass
238
+ add_widget "MenuPane", "inside"
239
+ elsif ["TabItem"].member? parentclass
240
+ add_widget "VerticalFrame", "after"
241
+ end
242
+ end
243
+
244
+ def rename_wdg wdg, newName
245
+ return unless wdg
246
+ begin
247
+ nil.instance_eval("@#{newName}", __FILE__, __LINE__) # check if newName is a suitable instance variable name
248
+ rescue Exception
249
+ puts $!
250
+ puts $!.backtrace
251
+ return
252
+ end
253
+ item = wdg.userData.treeitem
254
+ return if item.nil?
255
+ return if item == newName
256
+ if @w.has_key? newName
257
+ puts "<#{newName}> already exists!"
258
+ return
259
+ end
260
+ oldName = item.to_s
261
+ item.text = newName
262
+ wdg.wdg_name = newName
263
+ wdg.userData.treeitem = item
264
+ @w[newName] = wdg
265
+ @w.delete oldName if @w[oldName] == @w[newName]
266
+ @fxtree.update
267
+ true
268
+ end
269
+
270
+ def check_integrity type, parent
271
+ if type == "MenuPane" and !parent.respond_to?(:menu=)
272
+ raise "error: #{parent.class} cannot be parent to a MenuPane!"
273
+ elsif type == "MenuTitle" and !parent.is_a? FXToolBar
274
+ raise "not allowed: behavior undefined!"
275
+ elsif type == "MenuCascade" and !parent.is_a? FXPopup
276
+ raise "not allowed: behavior undefined!"
277
+ elsif type == "TabItem" and !parent.is_a? FXTabBar
278
+ raise 'error: #{parent.class} cannot hold a TabItem!'
279
+ end
280
+ end
281
+
282
+ # moves a widget in the browser and the gui
283
+ def relink_wdg item, where, relativeItem
284
+ assert(relativeItem, item, where)
285
+ wdg = @w[item.to_s]
286
+ parent = @w[relativeItem.parent.to_s]
287
+ relativeWdg = @w[relativeItem.to_s]
288
+ unless RelinkMechanisms.relink_possible?(wdg, where, relativeWdg)
289
+ puts "relink of #{wdg} #{where} #{relativeWdg} is not possible" # TODO log4r!
290
+ return
291
+ end
292
+ new = nil
293
+ if where == "before"
294
+ new = @fxtree.insertItem relativeItem, relativeItem.parent, FXTreeItem.new(item.to_s)
295
+ elsif where == "after"
296
+ new = if relativeItem.next
297
+ @fxtree.insertItem relativeItem.next, relativeItem.parent, FXTreeItem.new(item.to_s)
298
+ else
299
+ @fxtree.appendItem relativeItem.parent, FXTreeItem.new(item.to_s)
300
+ end
301
+ elsif where == "inside"
302
+ new = @fxtree.appendItem relativeItem, FXTreeItem.new(item.to_s)
303
+ else
304
+ return
305
+ end
306
+ expand_parents new
307
+ @fxtree.currentItem = new
308
+ a = []
309
+ subitem = nil
310
+ item.each { |subitem| a << subitem }
311
+ a.each { |subitem|
312
+ @fxtree.reparentItem subitem, new
313
+ }
314
+ RelinkMechanisms.relink_wdg wdg, where, relativeWdg
315
+ @fxtree.removeItem item
316
+ wdg.userData.treeitem = new # TODO don't use userData. better a singleton instance var
317
+ unmark
318
+ mark wdg
319
+ @fxtree.forceRefresh
320
+ wdg.shell.forceRefresh
321
+ set_dirty
322
+ wdg
323
+ end
324
+
325
+ # used to add a newly instanciated widget to the hashmap @widgets which
326
+ # associates widget names to widget references
327
+ # note: this method renames ambiguous names
328
+ def add_reference name, wdg
329
+ assert(wdg)
330
+ name = get_name(name)
331
+ @w[name] = wdg
332
+ wdg.set_name name
333
+ name
334
+ end
335
+
336
+ # makes shure a widget name is unique within the widgettree
337
+ # applies a simple renaming scheme and returns the unique name
338
+ def get_name(name)
339
+ begin
340
+ if @names.has_key? name
341
+ @names[name] += 1
342
+ else
343
+ @names[name] = 1
344
+ end
345
+ postfix = ""
346
+ postfix = @names[name].to_s if @names[name] > 0
347
+ wdg_name = name + postfix
348
+ end while(@w.has_key?(wdg_name)) # check, that name is unique
349
+ wdg_name
350
+ end
351
+
352
+ # sets the document as modified, as to ask the user to save on exit
353
+ def set_dirty
354
+ unless @dirty
355
+ @dirty = true
356
+ DocMan.instance.set_dirty self
357
+ end
358
+ end
359
+
360
+ def show # ????
361
+ MAINWIN.topwin.show(Fox::PLACEMENT_SCREEN)
362
+ end
363
+
364
+ # expands or collapses all subnodes of the item in the widget browser treelist
365
+ def expand item, bool = true
366
+ if bool
367
+ @fxtree.expandTree item
368
+ else
369
+ @fxtree.collapseTree item
370
+ end
371
+ item.each { |sub|
372
+ expand sub, bool
373
+ }
374
+ end
375
+
376
+ # expands all parents for the item to be shown
377
+ def expand_parents item
378
+ assert item
379
+ unless item.parent.nil?
380
+ @fxtree.expandTree item.parent
381
+ expand_parents item.parent
382
+ end
383
+ end
384
+
385
+ # colorizes the background of the currently selected widget.
386
+ # if marked allready the original color is restored
387
+ def mark wdg
388
+ return unless wdg
389
+ if wdg == @marked
390
+ wdg.hilighted = wdg.instance_eval { !@hilighted }
391
+ else
392
+ unmark
393
+ wdg.hilighted = true
394
+ @marked = wdg
395
+ $__wdg__ = @marked # for console debugging
396
+ end
397
+ end
398
+
399
+ # makes shure the current widget is not marked
400
+ def unmark
401
+ return unless @marked
402
+ @marked.hilighted = false
403
+ @marked = nil
404
+ end
405
+ end
@@ -0,0 +1,85 @@
1
+ ---
2
+ DND Events :
3
+ - SEL_BEGINDRAG
4
+ - SEL_ENDDRAG
5
+ - SEL_DRAGGED
6
+ - SEL_DND_ENTER
7
+ - SEL_DND_LEAVE
8
+ - SEL_DND_DROP
9
+ - SEL_DND_MOTION
10
+ - SEL_DND_REQUEST
11
+ Keyboard Events :
12
+ - SEL_KEYPRESS
13
+ - SEL_KEYRELEASE
14
+ - SEL_KEYMAP
15
+ Mouse Events :
16
+ - SEL_LEFTBUTTONPRESS
17
+ - SEL_LEFTBUTTONRELEASE
18
+ - SEL_MIDDLEBUTTONPRESS
19
+ - SEL_MIDDLEBUTTONRELEASE
20
+ - SEL_RIGHTBUTTONPRESS
21
+ - SEL_RIGHTBUTTONRELEASE
22
+ - SEL_MOTION
23
+ - SEL_CLICKED
24
+ - SEL_DOUBLECLICKED
25
+ - SEL_TRIPLECLICKED
26
+ - SEL_MOUSEWHEEL
27
+ Clipboard Events :
28
+ - SEL_CLIPBOARD_LOST
29
+ - SEL_CLIPBOARD_GAINED
30
+ - SEL_CLIPBOARD_REQUEST
31
+ Focus Events :
32
+ - SEL_FOCUS_SELF
33
+ - SEL_FOCUS_RIGHT
34
+ - SEL_FOCUS_LEFT
35
+ - SEL_FOCUS_DOWN
36
+ - SEL_FOCUS_UP
37
+ - SEL_FOCUS_NEXT
38
+ - SEL_FOCUS_PREV
39
+ - SEL_FOCUSIN
40
+ - SEL_FOCUSOUT
41
+ Window Events:
42
+ - SEL_ENTER
43
+ - SEL_LEAVE
44
+ - SEL_PAINT
45
+ - SEL_CREATE
46
+ - SEL_DESTROY
47
+ - SEL_UNMAP
48
+ - SEL_MAP
49
+ - SEL_CONFIGURE
50
+ - SEL_UPDATE
51
+ - SEL_RAISED
52
+ - SEL_LOWERED
53
+ - SEL_CLOSE
54
+ - SEL_DELETE
55
+ - SEL_MINIMIZE
56
+ - SEL_RESTORE
57
+ - SEL_MAXIMIZE
58
+ Common Events:
59
+ - SEL_COMMAND
60
+ - SEL_CHANGED
61
+ Selection Events:
62
+ - SEL_SELECTION_LOST
63
+ - SEL_SELECTION_GAINED
64
+ - SEL_SELECTION_REQUEST
65
+ - SEL_DESELECTED
66
+ - SEL_SELECTED
67
+ TreeList Events:
68
+ - SEL_EXPANDED
69
+ - SEL_COLLAPSED
70
+ Time Events:
71
+ - SEL_TIMEOUT
72
+ - SEL_CHORE
73
+ Other Events:
74
+ - SEL_SIGNAL
75
+ - SEL_LASSOED
76
+ - SEL_PICKED
77
+ - SEL_QUERY_TIP
78
+ - SEL_QUERY_HELP
79
+ - SEL_UNGRABBED
80
+ - SEL_VERIFY
81
+ - SEL_INSERTED
82
+ - SEL_REPLACED
83
+ - SEL_DELETED
84
+ - SEL_OPENED
85
+ - SEL_CLOSED
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2004-2006 by Henon (meinrad dot recheis at gmail dot com)
2
+
3
+ class Module
4
+ def __sends__ *args
5
+ args.each { |arg|
6
+ class_eval <<-CEEND, __FILE__, __LINE__ + 1
7
+ def on_#{arg}(&callback)
8
+ @#{arg}_observers ||= {}
9
+ @#{arg}_observers[caller[0]]=callback
10
+ return caller[0]
11
+ end
12
+ def del_#{arg}(id)
13
+ @#{arg}_observers ||= {}
14
+ return @#{arg}_observers.delete( id)
15
+ end
16
+ private
17
+ def #{arg} *the_args
18
+ @#{arg}_observers ||= {}
19
+ @#{arg}_observers.each { |caller, cb|
20
+ cb.call *the_args
21
+ }
22
+ end
23
+ CEEND
24
+ }
25
+ end
26
+ end
27
+
28
+ if __FILE__ == $0
29
+ class TextBox
30
+ __sends__ "text_changed", "key_pressed"
31
+
32
+ def initialize txt = ""
33
+ @txt = txt
34
+ end
35
+
36
+ def txt= txt
37
+ @txt = txt
38
+ text_changed
39
+ end
40
+
41
+ def key_press key
42
+ key_pressed key
43
+ end
44
+ end
45
+
46
+ box = TextBox.new
47
+ text_changed_id = box.on_text_changed { puts "Text changed!" }
48
+ 5.times { |i|
49
+ box.on_key_pressed { |k| puts "(#{i}) Key pressed: #{k}" }
50
+ }
51
+
52
+ box.txt = "New text!"
53
+ box.del_text_changed(text_changed_id)
54
+ box.txt = "New text!"
55
+
56
+ box.key_press "j"
57
+ end
data/src/events.yaml ADDED
@@ -0,0 +1,77 @@
1
+ ---
2
+ - SEL_ENDDRAG
3
+ - SEL_DRAGGED
4
+ - SEL_LASSOED
5
+ - SEL_TIMEOUT
6
+ - SEL_SIGNAL
7
+ - SEL_CLIPBOARD_LOST
8
+ - SEL_CLIPBOARD_GAINED
9
+ - SEL_CLIPBOARD_REQUEST
10
+ - SEL_CHORE
11
+ - SEL_FOCUS_SELF
12
+ - SEL_FOCUS_RIGHT
13
+ - SEL_FOCUS_LEFT
14
+ - SEL_FOCUS_DOWN
15
+ - SEL_FOCUS_UP
16
+ - SEL_FOCUS_NEXT
17
+ - SEL_FOCUS_PREV
18
+ - SEL_DND_ENTER
19
+ - SEL_KEYPRESS
20
+ - SEL_DND_LEAVE
21
+ - SEL_KEYRELEASE
22
+ - SEL_DND_DROP
23
+ - SEL_LEFTBUTTONPRESS
24
+ - SEL_DND_MOTION
25
+ - SEL_LEFTBUTTONRELEASE
26
+ - SEL_DND_REQUEST
27
+ - SEL_MIDDLEBUTTONPRESS
28
+ - SEL_IO_READ
29
+ - SEL_MIDDLEBUTTONRELEASE
30
+ - SEL_IO_WRITE
31
+ - SEL_RIGHTBUTTONPRESS
32
+ - SEL_IO_EXCEPT
33
+ - SEL_RIGHTBUTTONRELEASE
34
+ - SEL_PICKED
35
+ - SEL_MOTION
36
+ - SEL_QUERY_TIP
37
+ - SEL_ENTER
38
+ - SEL_QUERY_HELP
39
+ - SEL_LEAVE
40
+ - SEL_FOCUSIN
41
+ - SEL_FOCUSOUT
42
+ - SEL_KEYMAP
43
+ - SEL_UNGRABBED
44
+ - SEL_PAINT
45
+ - SEL_CREATE
46
+ - SEL_DESTROY
47
+ - SEL_UNMAP
48
+ - SEL_MAP
49
+ - SEL_CONFIGURE
50
+ - SEL_SELECTION_LOST
51
+ - SEL_SELECTION_GAINED
52
+ - SEL_SELECTION_REQUEST
53
+ - SEL_RAISED
54
+ - SEL_LOWERED
55
+ - SEL_CLOSE
56
+ - SEL_DELETE
57
+ - SEL_MINIMIZE
58
+ - SEL_RESTORE
59
+ - SEL_MAXIMIZE
60
+ - SEL_UPDATE
61
+ - SEL_COMMAND
62
+ - SEL_CLICKED
63
+ - SEL_DOUBLECLICKED
64
+ - SEL_TRIPLECLICKED
65
+ - SEL_MOUSEWHEEL
66
+ - SEL_CHANGED
67
+ - SEL_VERIFY
68
+ - SEL_DESELECTED
69
+ - SEL_SELECTED
70
+ - SEL_INSERTED
71
+ - SEL_REPLACED
72
+ - SEL_DELETED
73
+ - SEL_OPENED
74
+ - SEL_CLOSED
75
+ - SEL_EXPANDED
76
+ - SEL_COLLAPSED
77
+ - SEL_BEGINDRAG