reflexion 0.3.15 → 0.4.1

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.
data/src/ios/event.h CHANGED
@@ -4,7 +4,7 @@
4
4
  #define __REFLEX_SRC_IOS_EVENT_H__
5
5
 
6
6
 
7
- #import <UIKit/UIEvent.h>
7
+ #import <UIKit/UIKit.h>
8
8
  #include "../event.h"
9
9
 
10
10
 
@@ -12,6 +12,17 @@ namespace Reflex
12
12
  {
13
13
 
14
14
 
15
+ class NativeKeyEvent : public KeyEvent
16
+ {
17
+
18
+ public:
19
+
20
+ NativeKeyEvent (UIPress* press, Action action, int repeat = 0)
21
+ API_AVAILABLE(ios(13.4));
22
+
23
+ };// NativeKeyEvent
24
+
25
+
15
26
  class NativePointerEvent : public PointerEvent
16
27
  {
17
28
 
@@ -19,9 +30,22 @@ namespace Reflex
19
30
 
20
31
  NativePointerEvent (NSSet* touches, UIEvent* event, UIView* view);
21
32
 
33
+ NativePointerEvent (UIHoverGestureRecognizer* recognizer, UIView* view);
34
+
22
35
  };// NativePointerEvent
23
36
 
24
37
 
38
+ class NativeWheelEvent : public WheelEvent
39
+ {
40
+
41
+ public:
42
+
43
+ NativeWheelEvent (
44
+ UIPanGestureRecognizer* recognizer, UIView* view, CGPoint position);
45
+
46
+ };// NativeWheelEvent
47
+
48
+
25
49
  }// Reflex
26
50
 
27
51
 
data/src/ios/event.mm CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
 
5
5
  #include <assert.h>
6
+ #include <xot/util.h>
6
7
  #include "window.h"
7
8
 
8
9
 
@@ -11,7 +12,23 @@ namespace Reflex
11
12
 
12
13
 
13
14
  static uint
14
- get_type (UITouch* touch)
15
+ get_mouse_button_types (UIEvent* event)
16
+ {
17
+ if (!event) return 0;
18
+
19
+ uint ret = 0;
20
+ if (@available(iOS 13.4, *))
21
+ {
22
+ UIEventButtonMask mask = event.buttonMask;
23
+ if (mask & UIEventButtonMaskPrimary) ret |= Pointer::MOUSE_LEFT;
24
+ if (mask & UIEventButtonMaskSecondary) ret |= Pointer::MOUSE_RIGHT;
25
+ if (mask & UIEventButtonMaskForButtonNumber(3)) ret |= Pointer::MOUSE_MIDDLE;
26
+ }
27
+ return ret;
28
+ }
29
+
30
+ static uint
31
+ get_type (UITouch* touch, UIEvent* event)
15
32
  {
16
33
  assert(touch);
17
34
 
@@ -22,8 +39,15 @@ namespace Reflex
22
39
  {
23
40
  case UITouchTypeDirect: return Pointer::TOUCH;
24
41
  case UITouchTypePencil: return Pointer::PEN;
25
- default: return Pointer::TYPE_NONE;
26
42
  }
43
+
44
+ if (@available(iOS 13.4, *))
45
+ {
46
+ if (type == UITouchTypeIndirectPointer)
47
+ return Pointer::MOUSE | get_mouse_button_types(event);
48
+ }
49
+
50
+ return Pointer::TYPE_NONE;
27
51
  }
28
52
 
29
53
  static Pointer::Action
@@ -52,14 +76,8 @@ namespace Reflex
52
76
  }
53
77
 
54
78
  static uint
55
- get_modifiers (const UIEvent* event)
79
+ to_modifiers (NSInteger flags)
56
80
  {
57
- if (!event) return 0;
58
-
59
- NSInteger flags = 0;
60
- if (@available(iOS 13.4, *))
61
- flags = event.modifierFlags;
62
-
63
81
  return
64
82
  (flags & UIKeyModifierAlphaShift) ? MOD_CAPS : 0 |
65
83
  (flags & UIKeyModifierShift) ? MOD_SHIFT : 0 |
@@ -69,6 +87,18 @@ namespace Reflex
69
87
  (flags & UIKeyModifierNumericPad) ? MOD_NUMPAD : 0;
70
88
  }
71
89
 
90
+ static uint
91
+ get_modifiers (const UIEvent* event)
92
+ {
93
+ if (!event) return 0;
94
+
95
+ NSInteger flags = 0;
96
+ if (@available(iOS 13.4, *))
97
+ flags = event.modifierFlags;
98
+
99
+ return to_modifiers(flags);
100
+ }
101
+
72
102
  uint
73
103
  KeyEvent_get_modifiers ()
74
104
  {
@@ -76,25 +106,45 @@ namespace Reflex
76
106
  }
77
107
 
78
108
 
109
+ static const char*
110
+ get_chars (UIKey* key) API_AVAILABLE(ios(13.4))
111
+ {
112
+ NSString* s = key.characters;
113
+ if (!s || [s hasPrefix: @"UIKeyInput"])
114
+ return NULL;
115
+ return s.UTF8String;
116
+ }
117
+
118
+ NativeKeyEvent::NativeKeyEvent (UIPress* press, Action action, int repeat)
119
+ : KeyEvent(
120
+ action, get_chars(press.key), (int) press.key.keyCode,
121
+ to_modifiers(press.key.modifierFlags), repeat)
122
+ {
123
+ }
124
+
125
+
79
126
  NativePointerEvent::NativePointerEvent (
80
127
  NSSet* touches, UIEvent* event, UIView* view)
81
128
  {
82
129
  for (UITouch* touch in touches)
83
130
  {
84
131
  Pointer::Action action = get_action(touch);
132
+ uint type = get_type(touch, event);
85
133
 
86
134
  Pointer pointer(
87
135
  0,
88
- get_type(touch),
136
+ type,
89
137
  action,
90
138
  to_point([touch locationInView: view]),
91
139
  get_modifiers(event),
92
140
  (uint) touch.tapCount,
93
141
  action == Pointer::MOVE,
94
142
  touch.timestamp);
95
- Pointer_set_system_id(&pointer, (Pointer::ID) touch);
96
143
 
97
- if (pointer.action() != Pointer::DOWN)
144
+ if (!(type & Pointer::MOUSE))
145
+ Pointer_set_system_id(&pointer, (Pointer::ID) touch);
146
+
147
+ if (action != Pointer::DOWN)
98
148
  {
99
149
  Pointer_set_prev_position(
100
150
  &pointer, to_point([touch previousLocationInView: view]));
@@ -105,5 +155,38 @@ namespace Reflex
105
155
  }
106
156
  }
107
157
 
158
+ NativePointerEvent::NativePointerEvent (
159
+ UIHoverGestureRecognizer* recognizer, UIView* view)
160
+ {
161
+ assert(recognizer && view);
162
+
163
+ Pointer pointer(
164
+ 0,
165
+ Pointer::MOUSE,
166
+ Pointer::MOVE,
167
+ to_point([recognizer locationInView: view]),
168
+ get_modifiers(nil),
169
+ 0,
170
+ false,
171
+ Xot::time());
172
+
173
+ if (pointer)
174
+ PointerEvent_add_pointer(this, pointer);
175
+ }
176
+
177
+
178
+ NativeWheelEvent::NativeWheelEvent (
179
+ UIPanGestureRecognizer* recognizer, UIView* view, CGPoint position)
180
+ : WheelEvent(0, 0, 0, 0, 0, 0, get_modifiers(nil))
181
+ {
182
+ assert(recognizer && view);
183
+
184
+ WheelEvent_set_position(this, to_point(position));
185
+
186
+ CGPoint delta = [recognizer translationInView: view];
187
+ this->dposition().x = delta.x;
188
+ this->dposition().y = delta.y;
189
+ }
190
+
108
191
 
109
192
  }// Reflex
@@ -65,6 +65,14 @@ show_reflex_view_controller (
65
65
  [top presentViewController: reflex_vc animated: YES completion: nil];
66
66
  }
67
67
 
68
+ static BOOL
69
+ is_modifier_key (long keyCode) API_AVAILABLE(ios(13.4))
70
+ {
71
+ return
72
+ keyCode >= UIKeyboardHIDUsageKeyboardLeftControl &&
73
+ keyCode <= UIKeyboardHIDUsageKeyboardRightGUI;
74
+ }
75
+
68
76
 
69
77
  namespace global
70
78
  {
@@ -121,12 +129,18 @@ ReflexViewController_get_show_fun ()
121
129
  @end
122
130
 
123
131
 
124
- @interface ReflexViewController () <GLKViewDelegate>
132
+ @interface ReflexViewController () <GLKViewDelegate, UIGestureRecognizerDelegate>
125
133
 
126
134
  @property(nonatomic, strong) ReflexView* reflexView;
127
135
 
128
136
  @property(nonatomic, strong) CADisplayLink* displayLink;
129
137
 
138
+ @property(nonatomic, strong) UIHoverGestureRecognizer* hoverRecognizer;
139
+
140
+ @property(nonatomic, strong) UIPress* repeatingKeyPress;
141
+
142
+ @property(nonatomic, strong) NSTimer* repeatingKeyTimer;
143
+
130
144
  @end
131
145
 
132
146
 
@@ -136,6 +150,7 @@ ReflexViewController_get_show_fun ()
136
150
  Reflex::Window *pwindow, *ptr_for_rebind;
137
151
  int update_count;
138
152
  int touching_count;
153
+ int repeating_key_count;
139
154
  }
140
155
 
141
156
  - (id) init
@@ -143,10 +158,11 @@ ReflexViewController_get_show_fun ()
143
158
  self = [super init];
144
159
  if (!self) return nil;
145
160
 
146
- pwindow =
147
- ptr_for_rebind = NULL;
148
- update_count = 0;
149
- touching_count = 0;
161
+ pwindow =
162
+ ptr_for_rebind = NULL;
163
+ update_count = 0;
164
+ touching_count = 0;
165
+ repeating_key_count = 0;
150
166
 
151
167
  return self;
152
168
  }
@@ -264,15 +280,43 @@ ReflexViewController_get_show_fun ()
264
280
  //view.drawableMultisample = GLKViewDrawableMultisample4X;
265
281
 
266
282
  [self.view addSubview: view];
283
+ [self setupMouseRecognizers: view];
267
284
 
268
285
  self.reflexView = view;
269
286
  }
270
287
 
288
+ - (void) setupMouseRecognizers: (UIView*) view
289
+ {
290
+ if (@available(iOS 13.4, *))
291
+ {
292
+ UIHoverGestureRecognizer* hover = [[[UIHoverGestureRecognizer alloc]
293
+ initWithTarget: self action: @selector(hoverChanged:)]
294
+ autorelease];
295
+ hover.delegate = self;
296
+ [view addGestureRecognizer: hover];
297
+ self.hoverRecognizer = hover;
298
+
299
+ UIPanGestureRecognizer* pan = [[[UIPanGestureRecognizer alloc]
300
+ initWithTarget: self action: @selector(scrollChanged:)]
301
+ autorelease];
302
+ pan.allowedScrollTypesMask = UIScrollTypeMaskAll;
303
+ // 0/0 touches means the recognizer only fires from indirect scroll
304
+ // (mouse wheel / trackpad scroll) — touch-based panning is left to
305
+ // the touchesBegan/etc. path so this won't intercept finger pans.
306
+ pan.minimumNumberOfTouches = 0;
307
+ pan.maximumNumberOfTouches = 0;
308
+ pan.delegate = self;
309
+ [view addGestureRecognizer: pan];
310
+ }
311
+ }
312
+
271
313
  - (void) cleanupReflexView
272
314
  {
273
315
  ReflexView* view = self.reflexView;
274
316
  if (!view) return;
275
317
 
318
+ [self stopKeyRepeat];
319
+
276
320
  if (view.context && view.context == [EAGLContext currentContext])
277
321
  Rays::activate_offscreen_context();
278
322
 
@@ -299,10 +343,15 @@ ReflexViewController_get_show_fun ()
299
343
  selector: @selector(willResignActive)
300
344
  name: UIApplicationWillResignActiveNotification
301
345
  object: nil];
346
+
347
+ [self becomeFirstResponder];
302
348
  }
303
349
 
304
350
  - (void) viewDidDisappear: (BOOL) animated
305
351
  {
352
+ [self stopKeyRepeat];
353
+ [self resignFirstResponder];
354
+
306
355
  [NSNotificationCenter.defaultCenter
307
356
  removeObserver: self
308
357
  name: UIApplicationDidBecomeActiveNotification
@@ -325,6 +374,7 @@ ReflexViewController_get_show_fun ()
325
374
 
326
375
  - (void) willResignActive
327
376
  {
377
+ [self stopKeyRepeat];
328
378
  Window_call_deactivate_event(self.window);
329
379
  }
330
380
 
@@ -334,6 +384,11 @@ ReflexViewController_get_show_fun ()
334
384
  self.reflexView.frame = self.view.bounds;
335
385
  }
336
386
 
387
+ - (BOOL) canBecomeFirstResponder
388
+ {
389
+ return YES;
390
+ }
391
+
337
392
  - (void) startTimer
338
393
  {
339
394
  [self startTimer: 60];
@@ -509,4 +564,148 @@ ReflexViewController_get_show_fun ()
509
564
  Window_call_pointer_event(win, &e);
510
565
  }
511
566
 
567
+ - (void) hoverChanged: (UIHoverGestureRecognizer*) recognizer
568
+ API_AVAILABLE(ios(13.4))
569
+ {
570
+ Reflex::Window* win = self.window;
571
+ if (!win) return;
572
+
573
+ if (touching_count > 0) return;
574
+
575
+ if (
576
+ recognizer.state != UIGestureRecognizerStateBegan &&
577
+ recognizer.state != UIGestureRecognizerStateChanged)
578
+ {
579
+ return;
580
+ }
581
+
582
+ Reflex::NativePointerEvent e(recognizer, self.reflexView);
583
+ if (e.size() > 0)
584
+ Window_call_pointer_event(win, &e);
585
+ }
586
+
587
+ - (void) scrollChanged: (UIPanGestureRecognizer*) recognizer
588
+ API_AVAILABLE(ios(13.4))
589
+ {
590
+ Reflex::Window* win = self.window;
591
+ if (!win) return;
592
+
593
+ if (
594
+ recognizer.state != UIGestureRecognizerStateBegan &&
595
+ recognizer.state != UIGestureRecognizerStateChanged)
596
+ {
597
+ return;
598
+ }
599
+
600
+ // The pan recognizer's locationInView drifts with the synthetic scroll
601
+ // touch, so use the hover recognizer's location — it tracks the actual
602
+ // cursor and exposes the last known position regardless of its state.
603
+ CGPoint pos = [self.hoverRecognizer locationInView: self.reflexView];
604
+
605
+ Reflex::NativeWheelEvent e(recognizer, self.reflexView, pos);
606
+ Window_call_wheel_event(win, &e);
607
+
608
+ // reset translation so subsequent deltas are incremental
609
+ [recognizer setTranslation: CGPointZero inView: self.reflexView];
610
+ }
611
+
612
+ - (BOOL) gestureRecognizer: (UIGestureRecognizer*) recognizer
613
+ shouldRecognizeSimultaneouslyWithGestureRecognizer: (UIGestureRecognizer*) other
614
+ {
615
+ // UIKit only calls this delegate method when at least one of the two
616
+ // recognizers has us set as its delegate, and we only set ourselves as
617
+ // the delegate for our mouse recognizers (hover / indirect scroll). So
618
+ // every call here already involves one of ours, and we want it to run
619
+ // alongside whatever else is on the view (touches, app-added gestures).
620
+ return YES;
621
+ }
622
+
623
+ - (void) pressesBegan: (NSSet<UIPress*>*) presses
624
+ withEvent: (UIPressesEvent*) event
625
+ {
626
+ if (@available(iOS 13.4, *))
627
+ [self handlePresses: presses action: Reflex::KeyEvent::DOWN];
628
+ [super pressesBegan: presses withEvent: event];
629
+ }
630
+
631
+ - (void) pressesEnded: (NSSet<UIPress*>*) presses
632
+ withEvent: (UIPressesEvent*) event
633
+ {
634
+ if (@available(iOS 13.4, *))
635
+ [self handlePresses: presses action: Reflex::KeyEvent::UP];
636
+ [super pressesEnded: presses withEvent: event];
637
+ }
638
+
639
+ - (void) pressesCancelled: (NSSet<UIPress*>*) presses
640
+ withEvent: (UIPressesEvent*) event
641
+ {
642
+ if (@available(iOS 13.4, *))
643
+ [self handlePresses: presses action: Reflex::KeyEvent::UP];
644
+ [super pressesCancelled: presses withEvent: event];
645
+ }
646
+
647
+ - (void) handlePresses: (NSSet<UIPress*>*) presses
648
+ action: (Reflex::KeyEvent::Action) action
649
+ API_AVAILABLE(ios(13.4))
650
+ {
651
+ Reflex::Window* win = self.window;
652
+ if (!win) return;
653
+
654
+ for (UIPress* press in presses)
655
+ {
656
+ if (!press.key) continue;
657
+ Reflex::NativeKeyEvent e(press, action);
658
+ Window_call_key_event(win, &e);
659
+
660
+ if (action == Reflex::KeyEvent::DOWN)
661
+ {
662
+ if (!is_modifier_key(press.key.keyCode))
663
+ [self startKeyRepeat: press];
664
+ }
665
+ else if (
666
+ self.repeatingKeyPress &&
667
+ press.key.keyCode == self.repeatingKeyPress.key.keyCode)
668
+ {
669
+ [self stopKeyRepeat];
670
+ }
671
+ }
672
+ }
673
+
674
+ - (void) startKeyRepeat: (UIPress*) press
675
+ API_AVAILABLE(ios(13.4))
676
+ {
677
+ [self stopKeyRepeat];
678
+
679
+ repeating_key_count = 0;
680
+ self.repeatingKeyPress = press;
681
+ self.repeatingKeyTimer = [NSTimer scheduledTimerWithTimeInterval: 0.4
682
+ target: self selector: @selector(keyRepeatFired:) userInfo: nil repeats: NO];
683
+ }
684
+
685
+ - (void) keyRepeatFired: (NSTimer*) timer
686
+ API_AVAILABLE(ios(13.4))
687
+ {
688
+ Reflex::Window* win = self.window;
689
+ UIPress* press = self.repeatingKeyPress;
690
+ if (!win || !press || !press.key)
691
+ {
692
+ [self stopKeyRepeat];
693
+ return;
694
+ }
695
+
696
+ Reflex::NativeKeyEvent e(press, Reflex::KeyEvent::DOWN, ++repeating_key_count);
697
+ Window_call_key_event(win, &e);
698
+
699
+ self.repeatingKeyTimer = [NSTimer scheduledTimerWithTimeInterval: 0.1
700
+ target: self selector: @selector(keyRepeatFired:) userInfo: nil repeats: NO];
701
+ }
702
+
703
+ - (void) stopKeyRepeat
704
+ {
705
+ [self.repeatingKeyTimer invalidate];
706
+ self.repeatingKeyTimer = nil;
707
+ self.repeatingKeyPress = nil;
708
+ repeating_key_count = 0;
709
+ }
710
+
512
711
  @end// ReflexViewController
data/src/osx/event.h CHANGED
@@ -17,7 +17,7 @@ namespace Reflex
17
17
 
18
18
  public:
19
19
 
20
- NativeKeyEvent (NSEvent* event, Action action);
20
+ NativeKeyEvent (NSEvent* event, Action action, int repeat = 0);
21
21
 
22
22
  };// NativeKeyEvent
23
23
 
data/src/osx/event.mm CHANGED
@@ -52,10 +52,8 @@ namespace Reflex
52
52
  return [chars UTF8String];
53
53
  }
54
54
 
55
- NativeKeyEvent::NativeKeyEvent (NSEvent* e, Action action)
56
- : KeyEvent(
57
- action, get_chars(e), [e keyCode],
58
- get_modifiers(e), [e isARepeat] ? 1 : 0)
55
+ NativeKeyEvent::NativeKeyEvent (NSEvent* e, Action action, int repeat)
56
+ : KeyEvent(action, get_chars(e), [e keyCode], get_modifiers(e), repeat)
59
57
  {
60
58
  }
61
59
 
@@ -163,7 +161,7 @@ namespace Reflex
163
161
 
164
162
 
165
163
  NativeWheelEvent::NativeWheelEvent (NSEvent* e, NSView* view)
166
- : WheelEvent(0, 0, 0, [e deltaX], [e deltaY], [e deltaZ], get_modifiers(e))
164
+ : WheelEvent(0, 0, 0, [e deltaX], -[e deltaY], [e deltaZ], get_modifiers(e))
167
165
  {
168
166
  WheelEvent_set_position(this, get_pointer_position(e, view));
169
167
  }
@@ -56,6 +56,8 @@ move_to_main_screen_origin (NativeWindow* window)
56
56
  OpenGLView* view;
57
57
  NSTimer* timer;
58
58
  int update_count;
59
+ int repeating_key_code;
60
+ int repeating_key_count;
59
61
  }
60
62
 
61
63
  - (id) init
@@ -67,11 +69,13 @@ move_to_main_screen_origin (NativeWindow* window)
67
69
  defer: NO];
68
70
  if (!self) return nil;
69
71
 
70
- pwindow =
71
- ptr_for_rebind = NULL;
72
- view = nil;
73
- timer = nil;
74
- update_count = 0;
72
+ pwindow =
73
+ ptr_for_rebind = NULL;
74
+ view = nil;
75
+ timer = nil;
76
+ update_count = 0;
77
+ repeating_key_code = -1;
78
+ repeating_key_count = 0;
75
79
 
76
80
  [self setDelegate: self];
77
81
  [self setupContentView];
@@ -365,7 +369,16 @@ move_to_main_screen_origin (NativeWindow* window)
365
369
  Reflex::Window* win = self.window;
366
370
  if (!win) return;
367
371
 
368
- Reflex::NativeKeyEvent e(event, Reflex::KeyEvent::DOWN);
372
+ int code = (int) event.keyCode;
373
+ if (event.isARepeat && code == repeating_key_code)
374
+ ++repeating_key_count;
375
+ else
376
+ {
377
+ repeating_key_code = code;
378
+ repeating_key_count = 0;
379
+ }
380
+
381
+ Reflex::NativeKeyEvent e(event, Reflex::KeyEvent::DOWN, repeating_key_count);
369
382
  Window_call_key_event(win, &e);
370
383
  }
371
384
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reflexion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.15
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - xordog
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-09 00:00:00.000000000 Z
11
+ date: 2026-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xot
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.3.12
19
+ version: 0.3.13
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.3.12
26
+ version: 0.3.13
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rucy
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.3.12
33
+ version: 0.3.13
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.3.12
40
+ version: 0.3.13
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rays
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.3.12
47
+ version: 0.3.14
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.3.12
54
+ version: 0.3.14
55
55
  description: This library helps you to develop interactive graphical user interface.
56
56
  email: xordog@gmail.com
57
57
  executables: []