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.
@@ -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 add_observer(observer, property_name)
807
- property_listener_installers = self.class&.ancestors&.to_a.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
808
- widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
809
- widget_listener_installers.to_a.each do |widget_listener_installer|
810
- widget_listener_installer.call(observer)
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 apply_property_type_converters(attribute_name, args)
864
- if args.count == 1
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 property_type_converters
876
- color_converter = proc do |value|
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 widget_property_listener_installers
927
- @swt_widget_property_listener_installers ||= {
928
- # WidgetProxy => {
929
- # :focus => proc do |observer|
930
- # on_focus_gained { |focus_event|
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
- ScaleProxy => {
951
- :selection => proc do |observer|
952
- on_widget_selected { |selection_event|
953
- observer.call(selection)
954
- }
955
- end
505
+ 'select' => {
506
+ 'value' => 'onchange',
956
507
  },
957
- SliderProxy => {
958
- :selection => proc do |observer|
959
- on_widget_selected { |selection_event|
960
- observer.call(selection)
961
- }
962
- end
508
+ 'textarea' => {
509
+ 'value' => 'oninput',
963
510
  },
964
- SpinnerProxy => {
965
- :selection => proc do |observer|
966
- on_widget_selected { |selection_event|
967
- observer.call(selection)
968
- }
969
- end
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
- TextProxy => {
972
- :text => proc do |observer|
973
- on_modify_text { |modify_event|
974
- observer.call(text)
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
- # Java::OrgEclipseSwtCustom::StyledText => {
1030
- # :text => proc do |observer|
1031
- # on_modify_text { |modify_event|
1032
- # observer.call(getText)
1033
- # }
1034
- # end,
1035
- # },
1036
- DateTimeProxy => {
1037
- :date_time => proc do |observer|
1038
- on_widget_selected { |selection_event|
1039
- observer.call(date_time)
1040
- }
1041
- end
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
- RadioProxy => { #radio?
1044
- :selection => proc do |observer|
1045
- on_widget_selected { |selection_event|
1046
- observer.call(selection)
1047
- }
1048
- end
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
- TableProxy => {
1051
- :selection => proc do |observer|
1052
- on_widget_selected { |selection_event|
1053
- observer.call(selection_event.table_item.get_data) # TODO ensure selection doesn't conflict with editing
1054
- }
1055
- end,
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