glimmer-dsl-web 0.0.5 → 0.0.7
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 +4 -4
- data/CHANGELOG.md +18 -0
- data/LICENSE.txt +1 -1
- data/README.md +416 -61
- data/VERSION +1 -1
- data/glimmer-dsl-web.gemspec +11 -6
- data/lib/glimmer/data_binding/element_binding.rb +4 -4
- data/lib/glimmer/dsl/web/bind_expression.rb +36 -0
- data/lib/glimmer/dsl/web/data_binding_expression.rb +30 -0
- data/lib/glimmer/dsl/web/dsl.rb +6 -0
- data/lib/glimmer/dsl/web/element_expression.rb +2 -20
- data/lib/glimmer/dsl/web/general_element_expression.rb +29 -0
- data/lib/glimmer/dsl/web/p_expression.rb +2 -21
- data/lib/glimmer/dsl/web/select_expression.rb +12 -0
- data/lib/glimmer/dsl/web/shine_data_binding_expression.rb +42 -0
- data/lib/glimmer/util/proc_tracker.rb +1 -1
- data/lib/glimmer/web/element_proxy.rb +134 -589
- data/lib/glimmer/web/event_proxy.rb +1 -1
- data/lib/glimmer/web/listener_proxy.rb +1 -1
- data/lib/glimmer/web.rb +1 -1
- data/lib/glimmer-dsl-web/ext/date.rb +4 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_button.rb +1 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_data_binding.rb +181 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_form.rb +1 -1
- data/lib/glimmer-dsl-web/samples/hello/hello_input_date_time.rb +117 -0
- data/lib/glimmer-dsl-web/samples/hello/hello_world.rb +1 -1
- data/lib/glimmer-dsl-web.rb +1 -2
- metadata +9 -23
- data/lib/glimmer/web/property_owner.rb +0 -24
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2023 Andy Maleh
|
1
|
+
# Copyright (c) 2023-2024 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -19,12 +19,8 @@
|
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
21
|
|
22
|
-
# require 'glimmer/web/event_listener_proxy'
|
23
|
-
require 'glimmer/web/property_owner'
|
24
22
|
require 'glimmer/web/listener_proxy'
|
25
23
|
|
26
|
-
# TODO implement menu (which delays building it till render using add_content_on_render)
|
27
|
-
|
28
24
|
module Glimmer
|
29
25
|
module Web
|
30
26
|
class ElementProxy
|
@@ -68,11 +64,16 @@ module Glimmer
|
|
68
64
|
end
|
69
65
|
|
70
66
|
include Glimmer
|
71
|
-
include PropertyOwner
|
72
67
|
|
73
68
|
Event = Struct.new(:widget, keyword_init: true)
|
74
69
|
|
75
70
|
GLIMMER_ATTRIBUTES = [:parent]
|
71
|
+
FORMAT_DATETIME = '%Y-%m-%dT%H:%M'
|
72
|
+
FORMAT_DATE = '%Y-%m-%d'
|
73
|
+
FORMAT_TIME = '%H:%M'
|
74
|
+
REGEX_FORMAT_DATETIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/
|
75
|
+
REGEX_FORMAT_DATE = /^\d{4}-\d{2}-\d{2}$/
|
76
|
+
REGEX_FORMAT_TIME = /^\d{2}:\d{2}$/
|
76
77
|
|
77
78
|
attr_reader :keyword, :parent, :args, :options, :children, :enabled, :foreground, :background, :removed?, :rendered
|
78
79
|
alias rendered? rendered
|
@@ -186,7 +187,9 @@ module Glimmer
|
|
186
187
|
else
|
187
188
|
# TODO consider moving this to initializer
|
188
189
|
options[:parent] ||= 'body'
|
189
|
-
Document.find(options[:parent])
|
190
|
+
the_element = Document.find(options[:parent])
|
191
|
+
the_element = Document.find('body') if the_element.length == 0
|
192
|
+
the_element
|
190
193
|
end
|
191
194
|
end
|
192
195
|
|
@@ -284,360 +287,6 @@ module Glimmer
|
|
284
287
|
{}
|
285
288
|
end
|
286
289
|
|
287
|
-
def effective_observation_request_to_event_mapping
|
288
|
-
default_observation_request_to_event_mapping.merge(observation_request_to_event_mapping)
|
289
|
-
end
|
290
|
-
|
291
|
-
def default_observation_request_to_event_mapping
|
292
|
-
myself = self
|
293
|
-
mouse_event_handler = -> (event_listener) {
|
294
|
-
-> (event) {
|
295
|
-
# TODO generalize this solution to all widgets that support key presses
|
296
|
-
event.define_singleton_method(:widget) {myself}
|
297
|
-
event.define_singleton_method(:button, &event.method(:which))
|
298
|
-
event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
|
299
|
-
event.define_singleton_method(:x, &event.method(:page_x))
|
300
|
-
event.define_singleton_method(:y, &event.method(:page_y))
|
301
|
-
doit = true
|
302
|
-
event.define_singleton_method(:doit=) do |value|
|
303
|
-
doit = value
|
304
|
-
end
|
305
|
-
event.define_singleton_method(:doit) { doit }
|
306
|
-
|
307
|
-
if event.which == 1
|
308
|
-
# event.prevent # TODO consider if this is needed
|
309
|
-
event_listener.call(event)
|
310
|
-
end
|
311
|
-
|
312
|
-
# TODO Imlement doit properly for all different kinds of events
|
313
|
-
# unless doit
|
314
|
-
# event.prevent
|
315
|
-
# event.stop
|
316
|
-
# event.stop_immediate
|
317
|
-
# end
|
318
|
-
}
|
319
|
-
}
|
320
|
-
mouse_move_event_handler = -> (event_listener) {
|
321
|
-
-> (event) {
|
322
|
-
# TODO generalize this solution to all widgets that support key presses
|
323
|
-
event.define_singleton_method(:widget) {myself}
|
324
|
-
event.define_singleton_method(:button, &event.method(:which))
|
325
|
-
event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
|
326
|
-
event.define_singleton_method(:x, &event.method(:page_x))
|
327
|
-
event.define_singleton_method(:y, &event.method(:page_y))
|
328
|
-
doit = true
|
329
|
-
event.define_singleton_method(:doit=) do |value|
|
330
|
-
doit = value
|
331
|
-
end
|
332
|
-
event.define_singleton_method(:doit) { doit }
|
333
|
-
|
334
|
-
event_listener.call(event)
|
335
|
-
|
336
|
-
# TODO Imlement doit properly for all different kinds of events
|
337
|
-
# unless doit
|
338
|
-
# event.prevent
|
339
|
-
# event.stop
|
340
|
-
# event.stop_immediate
|
341
|
-
# end
|
342
|
-
}
|
343
|
-
}
|
344
|
-
context_menu_handler = -> (event_listener) {
|
345
|
-
-> (event) {
|
346
|
-
# TODO generalize this solution to all widgets that support key presses
|
347
|
-
event.define_singleton_method(:widget) {myself}
|
348
|
-
event.define_singleton_method(:button, &event.method(:which))
|
349
|
-
event.define_singleton_method(:count) {1} # TODO support double-click count of 2 in the future by using ondblclick
|
350
|
-
event.define_singleton_method(:x, &event.method(:page_x))
|
351
|
-
event.define_singleton_method(:y, &event.method(:page_y))
|
352
|
-
doit = true
|
353
|
-
event.define_singleton_method(:doit=) do |value|
|
354
|
-
doit = value
|
355
|
-
end
|
356
|
-
event.define_singleton_method(:doit) { doit }
|
357
|
-
|
358
|
-
if event.which == 3
|
359
|
-
event.prevent
|
360
|
-
event_listener.call(event)
|
361
|
-
end
|
362
|
-
# TODO Imlement doit properly for all different kinds of events
|
363
|
-
# unless doit
|
364
|
-
# event.prevent
|
365
|
-
# event.stop
|
366
|
-
# event.stop_immediate
|
367
|
-
# end
|
368
|
-
}
|
369
|
-
}
|
370
|
-
{
|
371
|
-
'on_focus_gained' => {
|
372
|
-
event: 'focus',
|
373
|
-
},
|
374
|
-
'on_focus_lost' => {
|
375
|
-
event: 'blur',
|
376
|
-
},
|
377
|
-
'on_mouse_move' => [
|
378
|
-
{
|
379
|
-
event: 'mousemove',
|
380
|
-
event_handler: mouse_move_event_handler,
|
381
|
-
},
|
382
|
-
],
|
383
|
-
'on_mouse_up' => [
|
384
|
-
{
|
385
|
-
event: 'mouseup',
|
386
|
-
event_handler: mouse_event_handler,
|
387
|
-
},
|
388
|
-
{
|
389
|
-
event: 'contextmenu',
|
390
|
-
event_handler: context_menu_handler,
|
391
|
-
},
|
392
|
-
],
|
393
|
-
'on_mouse_down' => [
|
394
|
-
{
|
395
|
-
event: 'mousedown',
|
396
|
-
event_handler: mouse_event_handler,
|
397
|
-
},
|
398
|
-
{
|
399
|
-
event: 'contextmenu',
|
400
|
-
event_handler: context_menu_handler,
|
401
|
-
},
|
402
|
-
],
|
403
|
-
'on_swt_mouseup' => [
|
404
|
-
{
|
405
|
-
event: 'mouseup',
|
406
|
-
event_handler: mouse_event_handler,
|
407
|
-
},
|
408
|
-
{
|
409
|
-
event: 'contextmenu',
|
410
|
-
event_handler: context_menu_handler,
|
411
|
-
},
|
412
|
-
],
|
413
|
-
'on_swt_mousedown' => [
|
414
|
-
{
|
415
|
-
event: 'mousedown',
|
416
|
-
event_handler: mouse_event_handler,
|
417
|
-
},
|
418
|
-
{
|
419
|
-
event: 'contextmenu',
|
420
|
-
event_handler: context_menu_handler,
|
421
|
-
},
|
422
|
-
],
|
423
|
-
'on_key_pressed' => {
|
424
|
-
event: 'keypress',
|
425
|
-
event_handler: -> (event_listener) {
|
426
|
-
-> (event) {
|
427
|
-
event.define_singleton_method(:widget) {myself}
|
428
|
-
event.define_singleton_method(:keyLocation) do
|
429
|
-
location = `#{event.to_n}.originalEvent.location`
|
430
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
431
|
-
end
|
432
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
433
|
-
event.define_singleton_method(:keyCode) {
|
434
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
435
|
-
}
|
436
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
437
|
-
event.define_singleton_method(:character) {event.which.chr}
|
438
|
-
event.define_singleton_method(:stateMask) do
|
439
|
-
state_mask = 0
|
440
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
441
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
442
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
443
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
444
|
-
state_mask
|
445
|
-
end
|
446
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
447
|
-
doit = true
|
448
|
-
event.define_singleton_method(:doit=) do |value|
|
449
|
-
doit = value
|
450
|
-
end
|
451
|
-
event.define_singleton_method(:doit) { doit }
|
452
|
-
event_listener.call(event)
|
453
|
-
|
454
|
-
# TODO Fix doit false, it's not stopping input
|
455
|
-
unless doit
|
456
|
-
event.prevent
|
457
|
-
event.prevent_default
|
458
|
-
event.stop_propagation
|
459
|
-
event.stop_immediate_propagation
|
460
|
-
end
|
461
|
-
|
462
|
-
doit
|
463
|
-
}
|
464
|
-
} },
|
465
|
-
'on_key_released' => {
|
466
|
-
event: 'keyup',
|
467
|
-
event_handler: -> (event_listener) {
|
468
|
-
-> (event) {
|
469
|
-
event.define_singleton_method(:keyLocation) do
|
470
|
-
location = `#{event.to_n}.originalEvent.location`
|
471
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
472
|
-
end
|
473
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
474
|
-
event.define_singleton_method(:widget) {myself}
|
475
|
-
event.define_singleton_method(:keyCode) {
|
476
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
477
|
-
}
|
478
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
479
|
-
event.define_singleton_method(:character) {event.which.chr}
|
480
|
-
event.define_singleton_method(:stateMask) do
|
481
|
-
state_mask = 0
|
482
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
483
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
484
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
485
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
486
|
-
state_mask
|
487
|
-
end
|
488
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
489
|
-
doit = true
|
490
|
-
event.define_singleton_method(:doit=) do |value|
|
491
|
-
doit = value
|
492
|
-
end
|
493
|
-
event.define_singleton_method(:doit) { doit }
|
494
|
-
event_listener.call(event)
|
495
|
-
|
496
|
-
# TODO Fix doit false, it's not stopping input
|
497
|
-
unless doit
|
498
|
-
event.prevent
|
499
|
-
event.prevent_default
|
500
|
-
event.stop_propagation
|
501
|
-
event.stop_immediate_propagation
|
502
|
-
end
|
503
|
-
|
504
|
-
doit
|
505
|
-
}
|
506
|
-
}
|
507
|
-
},
|
508
|
-
'on_swt_keydown' => [
|
509
|
-
{
|
510
|
-
event: 'keypress',
|
511
|
-
event_handler: -> (event_listener) {
|
512
|
-
-> (event) {
|
513
|
-
event.define_singleton_method(:keyLocation) do
|
514
|
-
location = `#{event.to_n}.originalEvent.location`
|
515
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
516
|
-
end
|
517
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
518
|
-
event.define_singleton_method(:keyCode) {
|
519
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
520
|
-
}
|
521
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
522
|
-
event.define_singleton_method(:widget) {myself}
|
523
|
-
event.define_singleton_method(:character) {event.which.chr}
|
524
|
-
event.define_singleton_method(:stateMask) do
|
525
|
-
state_mask = 0
|
526
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
527
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
528
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
529
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
530
|
-
state_mask
|
531
|
-
end
|
532
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
533
|
-
doit = true
|
534
|
-
event.define_singleton_method(:doit=) do |value|
|
535
|
-
doit = value
|
536
|
-
end
|
537
|
-
event.define_singleton_method(:doit) { doit }
|
538
|
-
event_listener.call(event)
|
539
|
-
|
540
|
-
# TODO Fix doit false, it's not stopping input
|
541
|
-
unless doit
|
542
|
-
event.prevent
|
543
|
-
event.prevent_default
|
544
|
-
event.stop_propagation
|
545
|
-
event.stop_immediate_propagation
|
546
|
-
end
|
547
|
-
|
548
|
-
doit
|
549
|
-
}
|
550
|
-
}
|
551
|
-
},
|
552
|
-
{
|
553
|
-
event: 'keydown',
|
554
|
-
event_handler: -> (event_listener) {
|
555
|
-
-> (event) {
|
556
|
-
event.define_singleton_method(:keyLocation) do
|
557
|
-
location = `#{event.to_n}.originalEvent.location`
|
558
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
559
|
-
end
|
560
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
561
|
-
event.define_singleton_method(:keyCode) {
|
562
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
563
|
-
}
|
564
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
565
|
-
event.define_singleton_method(:widget) {myself}
|
566
|
-
event.define_singleton_method(:character) {event.which.chr}
|
567
|
-
event.define_singleton_method(:stateMask) do
|
568
|
-
state_mask = 0
|
569
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
570
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
571
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
572
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
573
|
-
state_mask
|
574
|
-
end
|
575
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
576
|
-
doit = true
|
577
|
-
event.define_singleton_method(:doit=) do |value|
|
578
|
-
doit = value
|
579
|
-
end
|
580
|
-
event.define_singleton_method(:doit) { doit }
|
581
|
-
event_listener.call(event) if event.which != 13 && (event.which == 127 || event.which <= 40)
|
582
|
-
|
583
|
-
# TODO Fix doit false, it's not stopping input
|
584
|
-
unless doit
|
585
|
-
event.prevent
|
586
|
-
event.prevent_default
|
587
|
-
event.stop_propagation
|
588
|
-
event.stop_immediate_propagation
|
589
|
-
end
|
590
|
-
doit
|
591
|
-
}
|
592
|
-
}
|
593
|
-
}
|
594
|
-
],
|
595
|
-
'on_swt_keyup' => {
|
596
|
-
event: 'keyup',
|
597
|
-
event_handler: -> (event_listener) {
|
598
|
-
-> (event) {
|
599
|
-
event.define_singleton_method(:keyLocation) do
|
600
|
-
location = `#{event.to_n}.originalEvent.location`
|
601
|
-
JS_LOCATION_TO_SWT_KEY_LOCATION_MAP[location] || location
|
602
|
-
end
|
603
|
-
event.define_singleton_method(:key_location, &event.method(:keyLocation))
|
604
|
-
event.define_singleton_method(:widget) {myself}
|
605
|
-
event.define_singleton_method(:keyCode) {
|
606
|
-
JS_KEY_CODE_TO_SWT_KEY_CODE_MAP[event.which] || event.which
|
607
|
-
}
|
608
|
-
event.define_singleton_method(:key_code, &event.method(:keyCode))
|
609
|
-
event.define_singleton_method(:character) {event.which.chr}
|
610
|
-
event.define_singleton_method(:stateMask) do
|
611
|
-
state_mask = 0
|
612
|
-
state_mask |= SWTProxy[:alt] if event.alt_key
|
613
|
-
state_mask |= SWTProxy[:ctrl] if event.ctrl_key
|
614
|
-
state_mask |= SWTProxy[:shift] if event.shift_key
|
615
|
-
state_mask |= SWTProxy[:command] if event.meta_key
|
616
|
-
state_mask
|
617
|
-
end
|
618
|
-
event.define_singleton_method(:state_mask, &event.method(:stateMask))
|
619
|
-
doit = true
|
620
|
-
event.define_singleton_method(:doit=) do |value|
|
621
|
-
doit = value
|
622
|
-
end
|
623
|
-
event.define_singleton_method(:doit) { doit }
|
624
|
-
event_listener.call(event)
|
625
|
-
|
626
|
-
# TODO Fix doit false, it's not stopping input
|
627
|
-
unless doit
|
628
|
-
event.prevent
|
629
|
-
event.prevent_default
|
630
|
-
event.stop_propagation
|
631
|
-
event.stop_immediate_propagation
|
632
|
-
end
|
633
|
-
|
634
|
-
doit
|
635
|
-
}
|
636
|
-
}
|
637
|
-
},
|
638
|
-
}
|
639
|
-
end
|
640
|
-
|
641
290
|
def name
|
642
291
|
self.class.name.split('::').last.underscore.sub(/_proxy$/, '').gsub('_', '-')
|
643
292
|
end
|
@@ -765,35 +414,6 @@ module Glimmer
|
|
765
414
|
listener.register
|
766
415
|
listeners_for(keyword) << listener
|
767
416
|
listener
|
768
|
-
# return unless effective_observation_request_to_event_mapping.keys.include?(keyword)
|
769
|
-
# event = nil
|
770
|
-
# delegate = nil
|
771
|
-
# effective_observation_request_to_event_mapping[keyword].to_collection.each do |mapping|
|
772
|
-
# observation_requests[keyword] ||= Set.new
|
773
|
-
# observation_requests[keyword] << original_event_listener
|
774
|
-
# event = mapping[:event]
|
775
|
-
# event_handler = mapping[:event_handler]
|
776
|
-
# event_element_css_selector = mapping[:event_element_css_selector]
|
777
|
-
# potential_event_listener = event_handler&.call(original_event_listener)
|
778
|
-
# event_listener = potential_event_listener || original_event_listener
|
779
|
-
# async_event_listener = proc do |event|
|
780
|
-
## TODO look into the issue with using async::task.new here. maybe put it in event listener (like not being able to call preventDefault or return false successfully )
|
781
|
-
## maybe consider pushing inside the widget classes instead where needed only or implement universal doit support correctly to bypass this issue
|
782
|
-
## Async::Task.new do
|
783
|
-
# @@widget_handling_listener = self
|
784
|
-
## TODO also make sure to disable all widgets for suspension
|
785
|
-
# event_listener.call(event) unless dialog_ancestor&.event_handling_suspended?
|
786
|
-
# @widget_handling_listener = nil
|
787
|
-
## end
|
788
|
-
# end
|
789
|
-
# the_listener_dom_element = event_element_css_selector ? Element[event_element_css_selector] : listener_dom_element
|
790
|
-
# unless the_listener_dom_element.empty?
|
791
|
-
# the_listener_dom_element.on(event, &async_event_listener)
|
792
|
-
## TODO ensure uniqueness of insertion (perhaps adding equals/hash method to event listener proxy)
|
793
|
-
#
|
794
|
-
# event_listener_proxies << EventListenerProxy.new(element_proxy: self, selector: selector, dom_element: the_listener_dom_element, event: event, listener: async_event_listener, original_event_listener: original_event_listener)
|
795
|
-
# end
|
796
|
-
# end
|
797
417
|
end
|
798
418
|
|
799
419
|
def remove_event_listener_proxies
|
@@ -803,19 +423,27 @@ module Glimmer
|
|
803
423
|
event_listener_proxies.clear
|
804
424
|
end
|
805
425
|
|
806
|
-
def
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
426
|
+
def data_bind(property, model_binding)
|
427
|
+
element_binding_translator = value_converters_for_input_type(type)[:model_to_view]
|
428
|
+
element_binding_parameters = [self, property, element_binding_translator]
|
429
|
+
element_binding = DataBinding::ElementBinding.new(*element_binding_parameters)
|
430
|
+
element_binding.call(model_binding.evaluate_property)
|
431
|
+
#TODO make this options observer dependent and all similar observers in element specific data binding handlers
|
432
|
+
element_binding.observe(model_binding)
|
433
|
+
unless model_binding.binding_options[:read_only]
|
434
|
+
# TODO add guards against nil cases for hash below
|
435
|
+
listener_keyword = data_binding_listener_for_element_and_property(keyword, property)
|
436
|
+
if listener_keyword
|
437
|
+
data_binding_read_listener = lambda do |event|
|
438
|
+
view_property_value = send(property)
|
439
|
+
converted_view_property_value = value_converters_for_input_type(type)[:view_to_model].call(view_property_value, model_binding.evaluate_property)
|
440
|
+
model_binding.call(converted_view_property_value)
|
441
|
+
end
|
442
|
+
handle_observation_request(listener_keyword, data_binding_read_listener)
|
443
|
+
end
|
811
444
|
end
|
812
445
|
end
|
813
446
|
|
814
|
-
def set_attribute(attribute_name, *args)
|
815
|
-
apply_property_type_converters(attribute_name, args)
|
816
|
-
super(attribute_name, *args) # PropertyOwner
|
817
|
-
end
|
818
|
-
|
819
447
|
def respond_to_missing?(method_name, include_private = false)
|
820
448
|
# TODO consider doing more correct checking of availability of properties/methods using native `` ticks
|
821
449
|
property_name = property_name_for(method_name)
|
@@ -860,212 +488,129 @@ module Glimmer
|
|
860
488
|
self
|
861
489
|
end
|
862
490
|
|
863
|
-
def
|
864
|
-
|
865
|
-
value = args.first
|
866
|
-
converter = property_type_converters[attribute_name.to_sym]
|
867
|
-
args[0] = converter.call(value) if converter
|
868
|
-
end
|
869
|
-
# if args.count == 1 && args.first.is_a?(ColorProxy)
|
870
|
-
# g_color = args.first
|
871
|
-
# args[0] = g_color.swt_color
|
872
|
-
# end
|
491
|
+
def data_binding_listener_for_element_and_property(element_keyword, property)
|
492
|
+
data_binding_property_listener_map_for_element(element_keyword)[property]
|
873
493
|
end
|
874
494
|
|
875
|
-
def
|
876
|
-
|
877
|
-
if value.is_a?(Symbol) || value.is_a?(String)
|
878
|
-
ColorProxy.new(value)
|
879
|
-
else
|
880
|
-
value
|
881
|
-
end
|
882
|
-
end
|
883
|
-
@property_type_converters ||= {
|
884
|
-
:background => color_converter,
|
885
|
-
# :background_image => proc do |value|
|
886
|
-
# if value.is_a?(String)
|
887
|
-
# if value.start_with?('uri:classloader')
|
888
|
-
# value = value.sub(/^uri\:classloader\:\//, '')
|
889
|
-
# object = java.lang.Object.new
|
890
|
-
# value = object.java_class.resource_as_stream(value)
|
891
|
-
# value = java.io.BufferedInputStream.new(value)
|
892
|
-
# end
|
893
|
-
# image_data = ImageData.new(value)
|
894
|
-
# on_event_Resize do |resize_event|
|
895
|
-
# new_image_data = image_data.scaledTo(@swt_widget.getSize.x, @swt_widget.getSize.y)
|
896
|
-
# @swt_widget.getBackgroundImage&.remove
|
897
|
-
# @swt_widget.setBackgroundImage(Image.new(@swt_widget.getDisplay, new_image_data))
|
898
|
-
# end
|
899
|
-
# Image.new(@swt_widget.getDisplay, image_data)
|
900
|
-
# else
|
901
|
-
# value
|
902
|
-
# end
|
903
|
-
# end,
|
904
|
-
:foreground => color_converter,
|
905
|
-
# :font => proc do |value|
|
906
|
-
# if value.is_a?(Hash)
|
907
|
-
# font_properties = value
|
908
|
-
# FontProxy.new(self, font_properties).swt_font
|
909
|
-
# else
|
910
|
-
# value
|
911
|
-
# end
|
912
|
-
# end,
|
913
|
-
:text => proc do |value|
|
914
|
-
# if swt_widget.is_a?(Browser)
|
915
|
-
# value.to_s
|
916
|
-
# else
|
917
|
-
value.to_s
|
918
|
-
# end
|
919
|
-
end,
|
920
|
-
# :visible => proc do |value|
|
921
|
-
# !!value
|
922
|
-
# end,
|
923
|
-
}
|
495
|
+
def data_binding_property_listener_map_for_element(element_keyword)
|
496
|
+
data_binding_element_keyword_to_property_listener_map[element_keyword] || {}
|
924
497
|
end
|
925
498
|
|
926
|
-
def
|
927
|
-
@
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
# observer.call(true)
|
932
|
-
# }
|
933
|
-
# on_focus_lost { |focus_event|
|
934
|
-
# observer.call(false)
|
935
|
-
# }
|
936
|
-
# end,
|
937
|
-
# },
|
938
|
-
MenuItemProxy => {
|
939
|
-
:selection => proc do |observer|
|
940
|
-
on_widget_selected { |selection_event|
|
941
|
-
# TODO look into validity of this and perhaps move toggle logic to MenuItemProxy
|
942
|
-
if check?
|
943
|
-
observer.call(!selection)
|
944
|
-
else
|
945
|
-
observer.call(selection)
|
946
|
-
end
|
947
|
-
}
|
948
|
-
end
|
499
|
+
def data_binding_element_keyword_to_property_listener_map
|
500
|
+
@data_binding_element_keyword_to_property_listener_map ||= {
|
501
|
+
'input' => {
|
502
|
+
'value' => 'oninput',
|
503
|
+
'checked' => 'oninput',
|
949
504
|
},
|
950
|
-
|
951
|
-
|
952
|
-
on_widget_selected { |selection_event|
|
953
|
-
observer.call(selection)
|
954
|
-
}
|
955
|
-
end
|
505
|
+
'select' => {
|
506
|
+
'value' => 'onchange',
|
956
507
|
},
|
957
|
-
|
958
|
-
|
959
|
-
on_widget_selected { |selection_event|
|
960
|
-
observer.call(selection)
|
961
|
-
}
|
962
|
-
end
|
508
|
+
'textarea' => {
|
509
|
+
'value' => 'oninput',
|
963
510
|
},
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
511
|
+
}
|
512
|
+
end
|
513
|
+
|
514
|
+
def value_converters_for_input_type(input_type)
|
515
|
+
input_value_converters[input_type] || {model_to_view: ->(value, old_value) {value}, view_to_model: ->(value, old_value) {value}}
|
516
|
+
end
|
517
|
+
|
518
|
+
def input_value_converters
|
519
|
+
@input_value_converters ||= {
|
520
|
+
'number' => {
|
521
|
+
model_to_view: -> (value, old_value) { value.to_s },
|
522
|
+
view_to_model: -> (value, old_value) {
|
523
|
+
value.include?('.') ? value.to_f : value.to_i
|
524
|
+
},
|
970
525
|
},
|
971
|
-
|
972
|
-
:
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
end,
|
977
|
-
# :caret_position => proc do |observer|
|
978
|
-
# on_event_keydown { |event|
|
979
|
-
# observer.call(getCaretPosition)
|
980
|
-
# }
|
981
|
-
# on_event_keyup { |event|
|
982
|
-
# observer.call(getCaretPosition)
|
983
|
-
# }
|
984
|
-
# on_event_mousedown { |event|
|
985
|
-
# observer.call(getCaretPosition)
|
986
|
-
# }
|
987
|
-
# on_event_mouseup { |event|
|
988
|
-
# observer.call(getCaretPosition)
|
989
|
-
# }
|
990
|
-
# end,
|
991
|
-
# :selection => proc do |observer|
|
992
|
-
# on_event_keydown { |event|
|
993
|
-
# observer.call(getSelection)
|
994
|
-
# }
|
995
|
-
# on_event_keyup { |event|
|
996
|
-
# observer.call(getSelection)
|
997
|
-
# }
|
998
|
-
# on_event_mousedown { |event|
|
999
|
-
# observer.call(getSelection)
|
1000
|
-
# }
|
1001
|
-
# on_event_mouseup { |event|
|
1002
|
-
# observer.call(getSelection)
|
1003
|
-
# }
|
1004
|
-
# end,
|
1005
|
-
# :selection_count => proc do |observer|
|
1006
|
-
# on_event_keydown { |event|
|
1007
|
-
# observer.call(getSelectionCount)
|
1008
|
-
# }
|
1009
|
-
# on_event_keyup { |event|
|
1010
|
-
# observer.call(getSelectionCount)
|
1011
|
-
# }
|
1012
|
-
# on_event_mousedown { |event|
|
1013
|
-
# observer.call(getSelectionCount)
|
1014
|
-
# }
|
1015
|
-
# on_event_mouseup { |event|
|
1016
|
-
# observer.call(getSelectionCount)
|
1017
|
-
# }
|
1018
|
-
# end,
|
1019
|
-
# :top_index => proc do |observer|
|
1020
|
-
# @last_top_index = getTopIndex
|
1021
|
-
# on_paint_control { |event|
|
1022
|
-
# if getTopIndex != @last_top_index
|
1023
|
-
# @last_top_index = getTopIndex
|
1024
|
-
# observer.call(@last_top_index)
|
1025
|
-
# end
|
1026
|
-
# }
|
1027
|
-
# end,
|
526
|
+
'range' => {
|
527
|
+
model_to_view: -> (value, old_value) { value.to_s },
|
528
|
+
view_to_model: -> (value, old_value) {
|
529
|
+
value.include?('.') ? value.to_f : value.to_i
|
530
|
+
},
|
1028
531
|
},
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
532
|
+
'datetime-local' => {
|
533
|
+
model_to_view: -> (value, old_value) {
|
534
|
+
if value.respond_to?(:strftime)
|
535
|
+
value.strftime(FORMAT_DATETIME)
|
536
|
+
elsif value.is_a?(String) && valid_js_date_string?(value)
|
537
|
+
value
|
538
|
+
else
|
539
|
+
old_value
|
540
|
+
end
|
541
|
+
},
|
542
|
+
view_to_model: -> (value, old_value) {
|
543
|
+
if value.to_s.empty?
|
544
|
+
nil
|
545
|
+
else
|
546
|
+
date = Native(`new Date(Date.parse(#{value}))`)
|
547
|
+
year = Native.call(date, 'getFullYear')
|
548
|
+
month = Native.call(date, 'getMonth') + 1
|
549
|
+
day = Native.call(date, 'getDate')
|
550
|
+
hour = Native.call(date, 'getHours')
|
551
|
+
minute = Native.call(date, 'getMinutes')
|
552
|
+
Time.new(year, month, day, hour, minute)
|
553
|
+
end
|
554
|
+
},
|
1042
555
|
},
|
1043
|
-
|
1044
|
-
:
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
556
|
+
'date' => {
|
557
|
+
model_to_view: -> (value, old_value) {
|
558
|
+
if value.respond_to?(:strftime)
|
559
|
+
value.strftime(FORMAT_DATE)
|
560
|
+
elsif value.is_a?(String) && valid_js_date_string?(value)
|
561
|
+
value
|
562
|
+
else
|
563
|
+
old_value
|
564
|
+
end
|
565
|
+
},
|
566
|
+
view_to_model: -> (value, old_value) {
|
567
|
+
if value.to_s.empty?
|
568
|
+
nil
|
569
|
+
else
|
570
|
+
year, month, day = value.split('-')
|
571
|
+
if old_value
|
572
|
+
Time.new(year, month, day, old_value.hour, old_value.min)
|
573
|
+
else
|
574
|
+
Time.new(year, month, day)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
},
|
1049
578
|
},
|
1050
|
-
|
1051
|
-
:
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
579
|
+
'time' => {
|
580
|
+
model_to_view: -> (value, old_value) {
|
581
|
+
if value.respond_to?(:strftime)
|
582
|
+
value.strftime(FORMAT_TIME)
|
583
|
+
elsif value.is_a?(String) && valid_js_date_string?(value)
|
584
|
+
value
|
585
|
+
else
|
586
|
+
old_value
|
587
|
+
end
|
588
|
+
},
|
589
|
+
view_to_model: -> (value, old_value) {
|
590
|
+
if value.to_s.empty?
|
591
|
+
nil
|
592
|
+
else
|
593
|
+
hour, minute = value.split(':')
|
594
|
+
if old_value
|
595
|
+
Time.new(old_value.year, old_value.month, old_value.day, hour, minute)
|
596
|
+
else
|
597
|
+
now = Time.now
|
598
|
+
Time.new(now.year, now.month, now.day, hour, minute)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
},
|
1056
602
|
},
|
1057
|
-
# Java::OrgEclipseSwtWidgets::MenuItem => {
|
1058
|
-
# :selection => proc do |observer|
|
1059
|
-
# on_widget_selected { |selection_event|
|
1060
|
-
# observer.call(getSelection)
|
1061
|
-
# }
|
1062
|
-
# end
|
1063
|
-
# },
|
1064
603
|
}
|
1065
604
|
end
|
1066
605
|
|
1067
606
|
private
|
1068
607
|
|
608
|
+
def valid_js_date_string?(string)
|
609
|
+
[REGEX_FORMAT_DATETIME, REGEX_FORMAT_DATE, REGEX_FORMAT_TIME].any? do |format|
|
610
|
+
string.match(format)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
1069
614
|
def css_cursor
|
1070
615
|
SWT_CURSOR_TO_CSS_CURSOR_MAP[@cursor]
|
1071
616
|
end
|