glimmer-dsl-web 0.0.5 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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