glimmer-dsl-tk 0.0.44 → 0.0.48

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cb173f133301f49a6f4ddfc86578e450b563ff842b96274b6003b784b2e8acb
4
- data.tar.gz: 810087c40f044768c6394a968b945fdc2e3c4c0d1eb8caac2b3421259c431d61
3
+ metadata.gz: 8c99ac84c772515736f995d5a5ab31aaa38bbbe986d26d1c1566c8d49c5347c9
4
+ data.tar.gz: 192d47da74f8630c268f52f7601b67126cbe07d2284defc8a8a6174a068b6274
5
5
  SHA512:
6
- metadata.gz: 8842b20e509fd9c0f918953942665ac5a0609cbc91b0faf020c808e736890e1d2617abb579b384696711ecb0d40bf7ab82a5a0e669a1ff878bede9ed061c6abb
7
- data.tar.gz: 983740e737d7e066f56411ddea5ab7ab607bf196579d519e41aea07bd4c5e8d24b2dcd178086a3d8f35aa8e6ae7b0b8995cb27e3846516887ad4908e07758847
6
+ metadata.gz: 950ef28b2882ee22d7adf49528eb8fe0e3aa3c1949ea92ec319274af09bd5c10b5832a78ace63b1e77404fa26c9f0e0fe29ba78d398162150ca235b0441566fa
7
+ data.tar.gz: 65f424fc3206e9bbd30bbae63da5a249f259c4619d5e987b1ab19b5e608442f0502f3b9ec5b2aac92c6cf92dfc67fa08727297d8b06aee22c3d910a865458c01
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.0.48
4
+
5
+ - Moved `OS` class to `Glimmer::Tk::OS` to avoid clashing with os gem when installed for users
6
+ - Moved `DragAndDropEvent` from `Glimmer::Tk::DraggableAndDroppable` into `Glimmer::Tk` namespace directly as `Glimmer::Tk::DragAndDropEvent`
7
+
8
+ ## 0.0.47
9
+
10
+ - Upgrade to glimmer 2.5.1 (includes more advanced data-binding/observer features)
11
+
12
+ ## 0.0.46
13
+
14
+ - Support `drag_source true` on `list` widget
15
+ - Support `drop_target true` keyword alternative to `on_drop` for cases where it is implied what to drop (text in the case of label, entry, combo, list, and button)
16
+ - Support `on('drag_start')` (instead of `on_drag_start`), `on('drag_motion')`, and `on('drop')` for consistency with Glimmer DSL for general Tk listener style (`on('event')`)
17
+
18
+ ## 0.0.45
19
+
20
+ - Support `lbl` widget as the non-tile-themed version of `label` (i.e. `::TkLabel` not `::Tk::Tile::TLabel`)
21
+ - Add `#proxy` method to all Tk widgets to return Glimmer widget proxy objects (e.g. `Tk::Tile::TButton#proxy` returns `Glimmer::Tk::WidgetProxy` object)
22
+ - Provide question mark alias of `event.drop_accepted` (i.e. `drop_accepted?`)
23
+ - Have `DragAndDropEvent` `source`/`target` be the enhanced Glimmer widget proxy instead of the unenhanced Tk widget
24
+ - Look into improving code that uses `TkLabel` explicitly in Hello, Drag and Drop! (do `event.tooltip.content {label {...} }` with Glimmer DSL instead)
25
+ - Add Glimmer Style Guide
26
+ - Fix issue with dropping button and list unto checkbox in Hello, Drag and Drop!
27
+
3
28
  ## 0.0.44
4
29
 
5
30
  - Fix issue with not being able to drop list into checkbox in Hello, Drag and Drop! by disabling functionality for list just like button
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Tk 0.0.44
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Tk 0.0.48
2
2
  ## MRI Ruby Desktop Development GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-tk.svg)](http://badge.fury.io/rb/glimmer-dsl-tk)
4
4
  [![Ruby](https://github.com/AndyObtiva/glimmer-dsl-tk/actions/workflows/ruby.yml/badge.svg)](https://github.com/AndyObtiva/glimmer-dsl-tk/actions/workflows/ruby.yml)
@@ -53,7 +53,7 @@ Glimmer app:
53
53
 
54
54
  NOTE: Glimmer DSL for Tk is currently in early alpha mode (incomplete). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.
55
55
 
56
- Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
56
+ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems you might be interested in:
57
57
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
58
58
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
59
59
  - [glimmer-dsl-libui](https://github.com/AndyObtiva/glimmer-dsl-libui): Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
@@ -100,6 +100,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
100
100
  - [Radiobutton Data-Binding](#radiobutton-data-binding)
101
101
  - [Command Callback](#command-callback)
102
102
  - [Gotchas](#gotchas)
103
+ - [Glimmer Style Guide](#glimmer-style-guide)
103
104
  - [Samples](#samples)
104
105
  - [Hello, World!](#hello-world)
105
106
  - [Hello, Button!](#hello-button)
@@ -181,7 +182,7 @@ gem install glimmer-dsl-tk
181
182
 
182
183
  Add the following to `Gemfile`:
183
184
  ```
184
- gem 'glimmer-dsl-tk', '0.0.44'
185
+ gem 'glimmer-dsl-tk', '0.0.48'
185
186
  ```
186
187
 
187
188
  And, then run:
@@ -225,6 +226,8 @@ The Glimmer GUI DSL follows these simple concepts in mapping from Tk syntax:
225
226
  - **Content/Options Block**: Any keyword may optionally be followed by a Ruby curly-brace block containing nested widgets (content) and attributes (options). Attributes are simply Tk option keywords followed by arguments and no block (e.g. `title 'Hello, World!'` under a `root`)
226
227
  - **Event Binding Block**: `on(event) {}` keyword receiving a Tk binding event name (e.g. `KeyPress` or `ComboboxSelected`). Surrounding event by `<>` is optional as [Glimmer DSL for Tk](https://rubygems.org/gems/glimmer-dsl-tk) can take care of that automatically.
227
228
 
229
+ Note that Glimmer widgets are proxy objects (wrappers) for Tk widgets. To get wrapped Tk widget from a Glimmer widget, you simply invoke `#tk` method. To get the proxy of a wrapped Tk widget, you may invoke `#proxy` method.
230
+
228
231
  Example of an app written in [Tk](https://www.tcl.tk/) imperative syntax:
229
232
 
230
233
  ```ruby
@@ -303,6 +306,7 @@ keyword(args) | attributes | event bindings & callbacks
303
306
  `spinbox` | `text`, `from`, `to`, `increment`, `format`, [more attributes](https://tcl.tk/man/tcl8.6/TkCmd/text.htm#M116) | `command {}`, `'increment'`, `'decrement'`
304
307
  `frame(text: nil)` | `width`, `height`, `borderwidth`, `relief` (`'flat' (default), 'raised', 'sunken', 'solid', 'ridge', 'groove'`) | None
305
308
  `label` | `text`, `image` (optional keyword args: `subsample`, `zoom`, `from`, `to`, `shrink`, `compositingrule`), `compound` (`'center', 'top', 'bottom', 'left', 'right'`), `font` (`'default', 'text', 'fixed', 'menu', 'heading', 'caption', 'small_caption', 'icon', 'tooltip'`), `relief` (`'flat' (default), 'raised', 'sunken', 'solid', 'ridge', 'groove'`), `justify` (`'left', 'center', 'right'`), `foreground`, `background` | None
309
+ `lbl` (non-tile-themed version of `label`) | `text`, `image` (optional keyword args: `subsample`, `zoom`, `from`, `to`, `shrink`, `compositingrule`), `compound` (`'center', 'top', 'bottom', 'left', 'right'`), `font` (`'default', 'text', 'fixed', 'menu', 'heading', 'caption', 'small_caption', 'icon', 'tooltip'`), `relief` (`'flat' (default), 'raised', 'sunken', 'solid', 'ridge', 'groove'`), `justify` (`'left', 'center', 'right'`), `foreground`, `background`, `bg`, `bitmap`, [more here](https://tcl.tk/man/tcl8.6/TkCmd/label.htm) | None
306
310
  `list` | `selectmode`, `selection` | None
307
311
  `message_box(type: , message: , detail: , title: , icon: , default: , parent: )` | None | None
308
312
  `menu(label: nil) (label is nil if nested under root/toplevel for menu bar)` | None | None
@@ -476,14 +480,23 @@ Check out the [Hello, Scrollbar!](#hello-scrollbar) sample for a `text` demo wit
476
480
 
477
481
  #### Drag and Drop API
478
482
 
479
- Drag and drop works by simply designating a widget as a drag source with attribute `drag_source true`
483
+ Drag and drop works by simply designating a widget as a drag source with attribute `drag_source true` and another widget as a drop target with attribute `drop_target true`.
484
+
485
+ Alternatively, add listeners on the drag source:
486
+ - `on('drag_start') {|event| ...}`: fires on drag start receiving an `event` arg to set `data` and configure `source`
487
+ - `on('drag_motion') {|event| ...}`: fires on drag motion receiving an `event` arg to check `event#drop_accepted`, and configure `source` and `tooltip`
480
488
 
481
- Alternatively, add listeners:
482
- - `on_drag_start {|event| ...}`: fires on drag start receiving an `event` arg to set `data` and configure `source`
483
- - `on_drag_motion {|event| ...}`: fires on drag motion receiving an `event` arg to check `event#drop_accepted`, and configure `source` and `tooltip`
489
+ And on the drop target, add listener:
490
+ - `on('drop') { |event| ...}`: fires on drop, receiving an `event` arg with `event#target` and `event#data` (set during drag). You can even destroy the `event#source` if you want to get rid of the dragged widget.
484
491
 
485
- On the drop target, you simply define:
486
- - `on_drop { |event| ...}`: fires on drop, receiving an `event` arg with `event#target` and `event#data` (set during drag). You can even destroy the `event#source` if you want to get rid of the dragged widget.
492
+ These are all the available attributes on event, which is of type `DragAndDropEvent`:
493
+ - `source`: drag source widget proxy
494
+ - `target`: drop target widget proxy
495
+ - `tooltip`: tooltip widget proxy
496
+ - `x_root`: x coordinate from top-left root corner
497
+ - `y_root`: y coordinate from top-left root corner
498
+ - `data`: data being transferred through drag and drop
499
+ - `drop_accepted`: boolean indicating if drop was accepted
487
500
 
488
501
  Learn more at the [Hello, Drag and Drop!](#hello-drag-and-drop) sample.
489
502
 
@@ -824,6 +837,17 @@ More details can be found in the [Hello, Button!](#hello-button) sample below.
824
837
 
825
838
  - Setting `background` attribute on `frame` or `label` does not work in `'aqua'` theme on the Mac (only in `'classic'` theme)
826
839
 
840
+ ## Glimmer Style Guide
841
+
842
+ - Widget arguments are always wrapped by parentheses.
843
+ - Widget blocks are always declared with curly braces `{}` to clearly visualize hierarchical view code and separate from logic code.
844
+ - Widget attribute declarations always have arguments that are never wrapped inside parentheses and never take a block.
845
+ - Widget listeners are always declared with `on` keyword receiving listener event name as an argument. Their multi-line blocks have a `do; end` style to distinguish as logic from widget keywords and attributes.
846
+ - In a widget's content block, group attributes on top first, with the `grid` geometry management attribute being first, and separate by an empty line from nested widgets and listeners following afterwards.
847
+ - In a widget's content block, after attributes, you may either include nested widgets second and listeners third or vice versa.
848
+ - Unlike attributes, nested widgets and listeners are all separated by an empty line to make readability easier except where it helps to group two widgets together (e.g. label and described entry)
849
+ - Pure logic multi-line blocks that do not constitute GUI DSL view elements in general always have `do; end` style to clearly separate logic code from view code.
850
+
827
851
  ## Samples
828
852
 
829
853
  The easiest way to run samples is by launching the Glimmer Meta-Sample (the Sample of Samples).
@@ -1026,14 +1050,14 @@ class HelloButton
1026
1050
  text 'Text Button'
1027
1051
  }
1028
1052
 
1029
- button {
1053
+ button { |b|
1030
1054
  text <= [self, :count, on_read: ->(value) { "Click To Increment: #{value} " }]
1031
1055
  default 'active'
1032
1056
  focus true
1033
1057
 
1034
- command {
1058
+ on('command') do
1035
1059
  self.count += 1
1036
- }
1060
+ end
1037
1061
  }
1038
1062
  }
1039
1063
 
@@ -1048,9 +1072,9 @@ class HelloButton
1048
1072
  button {
1049
1073
  image File.expand_path('../../icons/glimmer.png', __dir__), subsample: 5
1050
1074
 
1051
- command {
1075
+ on('command') do
1052
1076
  message_box(title: 'Image Button', message: 'Image Button Clicked!')
1053
- }
1077
+ end
1054
1078
  }
1055
1079
  }
1056
1080
 
@@ -1065,12 +1089,12 @@ class HelloButton
1065
1089
  ['center', 'top', 'bottom', 'left', 'right'].each do |compound_option|
1066
1090
  button {
1067
1091
  image File.expand_path('../../icons/glimmer.png', __dir__), subsample: 5
1068
- text 'Text Image Button'
1092
+ text "#{compound_option.capitalize} Image"
1069
1093
  compound compound_option
1070
1094
 
1071
- command {
1095
+ on('command') do
1072
1096
  message_box(title: 'Text Image Button', message: 'Text Image Button Clicked!', detail: "(#{compound_option})")
1073
- }
1097
+ end
1074
1098
  }
1075
1099
  end
1076
1100
  }
@@ -2699,27 +2723,40 @@ root {
2699
2723
 
2700
2724
  label {
2701
2725
  grid :row => 0, :column => 0
2702
- text "Entry"
2726
+ text "Label"
2727
+ }
2728
+ label {
2729
+ grid :row => 0, :column => 1, :pady => 10, :sticky => "e"
2730
+ text "Drag label text"
2731
+ width 30
2732
+ drag_source true
2703
2733
  }
2704
2734
 
2735
+ label {
2736
+ grid :row => 1, :column => 0
2737
+ text "Entry"
2738
+ }
2705
2739
  entry {
2706
- grid :row => 0, :column => 1, :pady => 5, :sticky => "e"
2740
+ grid :row => 1, :column => 1, :pady => 5, :sticky => "e"
2707
2741
  text "Drag entry text"
2708
2742
  width 30
2709
2743
 
2710
- on_drag_start do |event|
2711
- event.data = event.source.textvariable&.value
2744
+ # drag_source true
2745
+ # This is how to do `drag_source true` the manual way for use in exceptional cases
2746
+ on('drag_start') do |event|
2747
+ event.data = event.source.text
2712
2748
  event.source.configure(:cursor => "hand2")
2713
- TkLabel.new(event.tooltip) {
2714
- text event.data + " "
2715
- bg "yellow"
2716
- bitmap "warning"
2717
- compound "right"
2718
- }.pack
2749
+ event.tooltip.content { # re-open tooltip content and add a label
2750
+ lbl { # non-tile-theme version of label
2751
+ text event.data + " "
2752
+ bg "yellow"
2753
+ bitmap "warning"
2754
+ compound "right"
2755
+ }
2756
+ }
2719
2757
  end
2720
-
2721
- on_drag_motion do |event|
2722
- if event.drop_accepted
2758
+ on('drag_motion') do |event|
2759
+ if event.drop_accepted?
2723
2760
  event.source.configure(:cursor => "hand1")
2724
2761
  else
2725
2762
  event.source.configure(:cursor => "hand2")
@@ -2728,48 +2765,34 @@ root {
2728
2765
  end
2729
2766
  }
2730
2767
 
2731
- label {
2732
- grid :row => 1, :column => 0
2733
- text "Label"
2734
- }
2735
-
2736
- label {
2737
- grid :row => 1, :column => 1, :pady => 10, :sticky => "e"
2738
- text "Drag label text"
2739
- width 30
2740
- drag_source true
2741
- }
2742
-
2743
2768
  label {
2744
2769
  grid :row => 2, :column => 0
2745
2770
  text "Combobox"
2746
2771
  }
2747
-
2748
2772
  combobox {
2749
2773
  grid :row => 2, :column => 1, :pady => 5, :sticky => "e"
2750
2774
  text "Spain"
2751
2775
  values %w[USA Canada Mexico Columbia UK Australia Germany Italy Spain]
2752
2776
  width 27
2753
2777
 
2754
- on_drag_start do |event|
2755
- event.data = event.source.textvariable&.value
2756
- end
2778
+ drag_source true
2757
2779
  }
2758
2780
 
2759
2781
  label {
2760
2782
  grid :row => 3, :column => 0
2761
2783
  text 'List'
2762
2784
  }
2763
-
2764
- country_list = list {
2785
+ list {
2765
2786
  grid :row => 3, :column => 1, :pady => 5, :sticky => "e"
2766
2787
  selectmode 'browse'
2767
2788
  items %w[USA Canada Mexico]
2768
2789
  selection 'Canada'
2769
2790
  height 3
2770
2791
 
2771
- on_drag_start do |event|
2772
- event.data = event.source.selection.to_a.first.text
2792
+ # drag_source true
2793
+ # This is another alternative to `drag_source true` with manual specification of transferred data only
2794
+ on('drag_start') do |event|
2795
+ event.data = event.source.selection.first
2773
2796
  end
2774
2797
  }
2775
2798
 
@@ -2777,7 +2800,6 @@ root {
2777
2800
  grid :row => 4, :column => 0
2778
2801
  text "Button"
2779
2802
  }
2780
-
2781
2803
  button {
2782
2804
  grid :row => 4, :column => 1, :pady => 5, :sticky => "w"
2783
2805
  text "Drag it"
@@ -2792,89 +2814,76 @@ root {
2792
2814
 
2793
2815
  label {
2794
2816
  grid :row => 0, :column => 0
2795
- text "Entry"
2817
+ text "Label"
2796
2818
  }
2797
-
2798
- entry {
2799
- grid :row => 0, :column => 1, :pady => 5, :sticky => "e"
2819
+ label {
2820
+ grid :row => 0, :column => 1, :pady => 10, :sticky => "e"
2800
2821
  width 30
2822
+ borderwidth 2
2823
+ relief "solid"
2801
2824
 
2802
- on_drop { |event|
2803
- event.target.textvariable.value = event.data
2804
- }
2825
+ drop_target true
2805
2826
  }
2806
2827
 
2807
2828
  label {
2808
2829
  grid :row => 1, :column => 0
2809
- text "Label"
2830
+ text "Entry"
2810
2831
  }
2811
-
2812
- label {
2813
- grid :row => 1, :column => 1, :pady => 10, :sticky => "e"
2832
+ entry {
2833
+ grid :row => 1, :column => 1, :pady => 5, :sticky => "e"
2814
2834
  width 30
2815
- borderwidth 2
2816
- relief "solid"
2817
2835
 
2818
- on_drop do |event|
2819
- event.target.textvariable.value = event.data
2820
- end
2836
+ drop_target true
2821
2837
  }
2822
2838
 
2823
2839
  label {
2824
2840
  grid :row => 2, :column => 0
2825
2841
  text "Combobox"
2826
2842
  }
2827
-
2828
2843
  combobox {
2829
2844
  grid :row => 2, :column => 1, :pady => 5, :sticky => "e"
2830
2845
  width 27
2831
2846
 
2832
- on_drop do |event|
2833
- event.target.textvariable.value = event.data
2834
- end
2847
+ drop_target true
2835
2848
  }
2836
2849
 
2837
2850
  label {
2838
2851
  grid :row => 3, :column => 0
2839
2852
  text 'List'
2840
2853
  }
2841
-
2842
2854
  list {
2843
2855
  grid :row => 3, :column => 1, :pady => 5, :sticky => "e"
2844
2856
  selectmode 'browse'
2845
2857
  height 3
2846
2858
 
2847
- on_drop do |event|
2848
- event.target.insert('', 'end', :text => event.data)
2849
- end
2859
+ drop_target true
2850
2860
  }
2851
2861
 
2852
2862
  label {
2853
2863
  grid :row => 4, :column => 0
2854
2864
  text "Button"
2855
2865
  }
2856
-
2857
2866
  button {
2858
2867
  grid :row => 4, :column => 1, :pady => 5, :sticky => "w"
2859
2868
  text "Drop here"
2860
2869
 
2861
- on_drop do |event|
2862
- event.target.text = event.data
2863
- end
2870
+ drop_target true
2864
2871
  }
2865
2872
 
2866
2873
  label {
2867
2874
  grid :row => 5, :column => 0
2868
2875
  text "Checkbutton"
2869
2876
  }
2870
-
2871
2877
  checkbutton {
2872
2878
  grid :row => 5, :column => 1, :pady => 5, :sticky => "w"
2873
- text "Drop here to destroy a widget\n(except button)"
2874
-
2875
- on_drop do |event|
2879
+ text "Drop here to destroy a widget"
2880
+
2881
+ # drop_target true
2882
+ # This is an alternative to `drop_target true` with manual consumption of transferred data
2883
+ on('drop') do |event|
2876
2884
  event.target.text = event.data
2877
- event.source.destroy unless event.source.is_a? Tk::Button
2885
+ # execute asynchronously after 100ms to ensure all events have been processed before destruction
2886
+ ::Tk.after(100) {event.source.destroy}
2878
2887
  end
2879
2888
  }
2880
2889
  }
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.44
1
+ 0.0.48
data/bin/girb CHANGED
File without changes
Binary file
@@ -50,7 +50,7 @@ module Glimmer
50
50
  #TODO check if nested data binding works for list widget and other widgets that need custom data binding
51
51
  list_selection_binding.observe(model, model_binding.property_name_expression)
52
52
 
53
- parent.tk.bind('<TreeviewSelect>') do
53
+ parent.on('<TreeviewSelect>') do
54
54
  model_binding.call(list_selection_binding.evaluate_property)
55
55
  end
56
56
  end
@@ -0,0 +1,23 @@
1
+ module Glimmer
2
+ module Tk
3
+ DragAndDropEvent = Struct.new(:source, :target, :tooltip, :x_root, :y_root, :data, :drop_accepted) do
4
+ alias drop_accepted? drop_accepted
5
+
6
+ def as_json(*)
7
+ klass = self.class.name
8
+ {
9
+ JSON.create_id => klass,
10
+ "v" => [values[0].object_id, values[1].object_id, values[2].object_id].concat(values.drop 3),
11
+ }
12
+ end
13
+
14
+ def to_json(*args)
15
+ as_json.to_json(*args)
16
+ end
17
+
18
+ def self.json_create(object)
19
+ new(*[ObjectSpace._id2ref(object["v"][0]), ObjectSpace._id2ref(object["v"][1]).proxy, ObjectSpace._id2ref(object["v"][2])].concat(object["v"].drop 3))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,165 @@
1
+ # Copyright (c) 2020-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require_relative 'drag_and_drop_event'
23
+ require "json"
24
+
25
+ module Glimmer
26
+ module Tk
27
+ module DraggableAndDroppable
28
+ attr_accessor :on_drag_motion_block
29
+
30
+ def handle_listener(listener_name, &listener)
31
+ case listener_name.to_s.downcase
32
+ when 'drag_start'
33
+ self.on_drag_start_block = listener
34
+ when 'drag_motion'
35
+ self.on_drag_motion_block = listener
36
+ when 'drop'
37
+ self.on_drop_block = listener
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ def drag_source=(value)
44
+ @drag_source = value
45
+ if @drag_source
46
+ make_draggable
47
+ else
48
+ make_non_draggable
49
+ end
50
+ end
51
+
52
+ def drop_target=(value)
53
+ @drop_target = value
54
+ if @drop_target
55
+ make_droppable
56
+ else
57
+ make_non_droppable
58
+ end
59
+ end
60
+
61
+ def on_drag_start_block=(block)
62
+ @on_drag_start_block = block
63
+ make_draggable
64
+ end
65
+
66
+ def on_drop_block=(value)
67
+ @on_drop_block = value
68
+ self.tk.bind("<DropEvent>", proc { |tk_event|
69
+ drop_event = Glimmer::Tk::DragAndDropEvent.json_create(JSON.parse("{" + tk_event.detail + "}"))
70
+ @on_drop_block.call(drop_event) if self == drop_event.target
71
+ })
72
+ self.tk.bind("<DropCheckEvent>", proc { |tk_event|
73
+ drop_check_event = Glimmer::Tk::DragAndDropEvent.json_create(JSON.parse("{" + tk_event.detail + "}"))
74
+ drop_check_event.source.event_generate("<DropAcceptedEvent>")
75
+ })
76
+ end
77
+
78
+ def textvariable_defined?
79
+ tk_widget_has_attribute_setter?(:textvariable) && !tk.textvariable.nil?
80
+ end
81
+
82
+ def make_draggable
83
+ drag_event = nil
84
+ bind("<DropAcceptedEvent>", proc { |event| drag_event.drop_accepted = true })
85
+ bind("B1-Motion", proc { |tk_event|
86
+ if drag_event.nil?
87
+ tooltip = WidgetProxy.new('toplevel', root_parent_proxy, [])
88
+ tooltip.overrideredirect(1) #create tooltip window to display dragged data
89
+ tooltip.geometry("+#{tk_event.x_root + 10}+#{tk_event.y_root - 2}")
90
+ drag_event = Glimmer::Tk::DragAndDropEvent.new(self, nil, tooltip, tk_event.x_root, tk_event.y_root, nil, false)
91
+ if @drag_source
92
+ tk_event.widget.configure(:cursor => "hand2")
93
+ # Default data to drag is text
94
+ drag_event.data = if textvariable_defined?
95
+ tk.textvariable.value
96
+ elsif has_attribute?('text')
97
+ tk.text
98
+ elsif has_attribute?('selection')
99
+ selection.first
100
+ end
101
+ tooltip_label = WidgetProxy.new('label', tooltip, [])
102
+ tooltip_label.text = drag_event.data
103
+ tooltip_label.pack # TODO look into using grid instead to be consistent with the modern Tk way
104
+ elsif !@on_drag_start_block.nil?
105
+ @on_drag_start_block.call(drag_event)
106
+ if tooltip.winfo_children().length == 0
107
+ tooltip_label = WidgetProxy.new('label', tooltip, [])
108
+ tooltip_label.text = drag_event.data
109
+ tooltip_label.pack # TODO look into using grid instead to be consistent with the modern Tk way
110
+ end
111
+ end
112
+ else
113
+ drag_event.x_root, drag_event.y_root = tk_event.x_root, tk_event.y_root
114
+ drag_event.drop_accepted = false
115
+ move_over_widget = tk_event.widget.winfo_containing(tk_event.x_root, tk_event.y_root)
116
+ drag_event.target = move_over_widget.proxy
117
+ move_over_widget.event_generate("<DropCheckEvent>", :data => drag_event.to_json)
118
+ if @on_drag_motion_block.nil?
119
+ # Default motion behavior:
120
+ # 1.Change cursor to show whether text can be dropped.
121
+ # 2.Move tooltip with dragged data.
122
+ if drag_event.drop_accepted
123
+ tk_event.widget.configure(:cursor => "hand1")
124
+ else
125
+ tk_event.widget.configure(:cursor => "hand2")
126
+ end
127
+ drag_event.tooltip.geometry("+#{tk_event.x_root + 10}+#{tk_event.y_root - 2}")
128
+ else
129
+ @on_drag_motion_block.call(drag_event)
130
+ end
131
+ end
132
+ })
133
+ bind("ButtonRelease-1", proc { |tk_event|
134
+ if drag_event
135
+ drag_event.target = tk_event.widget.winfo_containing(tk_event.x_root, tk_event.y_root).proxy
136
+ drag_event.source.configure(:cursor => "")
137
+ drag_event.target.event_generate("<DropEvent>", :data => drag_event.to_json)
138
+ drag_event.tooltip.destroy
139
+ drag_event = nil
140
+ end
141
+ })
142
+ end
143
+
144
+ def make_non_draggable
145
+ @tk.bind_remove("B1-Motion")
146
+ @tk.bind_remove("ButtonRelease-1")
147
+ @tk.bind_remove("<DropAcceptedEvent>")
148
+ end
149
+
150
+ def make_droppable
151
+ self.on_drop_block = Proc.new do |event|
152
+ event.target.text = event.data if event.target.has_attribute?('text')
153
+ event.target.items += [event.data] if event.target.has_attribute?('items')
154
+ event.target.selection += [event.data] if event.target.has_attribute?('selection')
155
+ end
156
+ end
157
+
158
+ def make_non_droppable
159
+ @tk.bind_remove('<DropEvent>')
160
+ @tk.bind_remove('<DropCheckEvent>')
161
+ @on_drop_block = nil
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2020-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/tk/widget_proxy'
23
+
24
+ module Glimmer
25
+ module Tk
26
+ # Non-Themable (non-Tile) Tk Label
27
+ class LblProxy < WidgetProxy
28
+ def build_widget
29
+ @tk = ::TkLabel.new(@parent_proxy.tk, *args).tap {|tk| tk.singleton_class.include(Glimmer::Tk::Widget); tk.proxy = self}
30
+ end
31
+ end
32
+ end
33
+ end