glimmer-dsl-tk 0.0.37 → 0.0.38

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: 2454b5a3d7bd7159328cab0c0c82a4ebcdb54066bec28a1549e3cbca61d1bd8d
4
- data.tar.gz: 7535984b2838617c7a40ee336c653e6c5c9029c7e49b3a4a13f1815897ecec8d
3
+ metadata.gz: 5c6969620ced6ade48e45e36bd48909bfe282a412e2f57d8a201f398082c6836
4
+ data.tar.gz: d8d4bc858b5fdf7399b10d71922a2e12aaf1cbf0b4c8c0db4bf78416e025a5d0
5
5
  SHA512:
6
- metadata.gz: d377872669979a76bf149aec8683ade86fadd7efa50817abe4f8e582a0f3b53ac354759f3e7e6aa4e683fcd6296308eb7339c049718fcd7c673e4452f1052614
7
- data.tar.gz: b321a3e34b16f9a1719797c51f25521a48c4c12ba39f92a730618dedc54f564754f24e73f60118023213583e3629c188643684cc7d326628613c982c0a87e7d0
6
+ metadata.gz: 35a7d92e7f5b9098a9dcd50a5045447237bd66b89dfdd9f5ea10d45201b050a66b7b272266f275645552980a043cda9b59b6b96f591521a7da326fce4ff60524
7
+ data.tar.gz: c866f8e9a51d4dd291900ebb0c3b506f5a8d0975d1df779474ebf0997114d4384f5a9d2610e868a3196c1ba1b93cfeb98311d41f4e2b35b1582c64cee2b9dc71
data/CHANGELOG.md CHANGED
@@ -1,7 +1,18 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.0.38
4
+
5
+ - Hello, Menu Bar! sample
6
+ - Support `menu` and `menu_item` widgets including `:command` (default), `:separator`, `:radiobutton`, and `:checkbutton` styles
7
+ - Support menu item `accelerator` attribute
8
+ - Support menu item `selection` attribute
9
+ - Support menu item `state` attribute
10
+ - Support menu item command event, which is triggered on mouse/keyboard menu item selection or via accelerator
11
+ - Support `toplevel` `iconphoto` (more conveniently via direct path) and `resizable` attributes
12
+
3
13
  ## 0.0.37
4
14
 
15
+ - Support `focus: true` or `focus: [integer]` option for all selection formatting methods (`add_selection_format`, `remove_selection_format`, `toggle_selection_format`, `add_selection_font_format`, `remove_selection_font_format`, `toggle_selection_font_format`)
5
16
  - Have `text` widget grab focus after every formatting modification in Hello, Text! (e.g. after clicking `B` for Bold)
6
17
 
7
18
  ## 0.0.36
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.37
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.38
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)
@@ -57,7 +57,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
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)
60
- - [glimmer-dsl-gtk](https://github.com/AndyObtiva/glimmer-dsl-gtk): Ruby-GNOME Desktop Development GUI Library
60
+ - [glimmer-dsl-gtk](https://github.com/AndyObtiva/glimmer-dsl-gtk): Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)
61
61
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
62
62
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets)
63
63
 
@@ -82,6 +82,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
82
82
  - [Image Attribute](#image-attribute)
83
83
  - [Frame Padding](#frame-padding)
84
84
  - [Notebook Frame](#notebook-frame)
85
+ - [Menu Item Accelerator](#menu-item-accelerator)
85
86
  - [Icon Photo](#icon-photo)
86
87
  - [Root Background](#root-background)
87
88
  - [Toplevel Mac Style](#toplevel-mac-style)
@@ -120,10 +121,12 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
120
121
  - [Hello, Drag and Drop!](#hello-drag-and-drop)
121
122
  - [Hello, Built-in Dialog!](#hello-built-in-dialog)
122
123
  - [Hello, Separator!](#hello-separator)
124
+ - [Hello, Menu Bar!](#hello-menu-bar)
123
125
  - [Applications](#applications)
124
126
  - [Glimmer Tk Calculator](#glimmer-tk-calculator)
125
127
  - [Y3network Ruby UI](#y3network-ruby-ui)
126
128
  - [CryptoPunks GUI](#cryptopunks-gui)
129
+ - [Circule](#circule)
127
130
  - [Process](#process)
128
131
  - [Resources](#resources)
129
132
  - [Help](#help)
@@ -146,6 +149,13 @@ For example, on the Mac, you can:
146
149
  - Run: `rvm install 3.0.2 --enable-shared --enable-pthread --with-tk --with-tcl`
147
150
  - Run: `gem install tk -v0.4.0`
148
151
 
152
+ For example, on Windows, you can follow the [instructions over here](https://tkdocs.com/tutorial/install.html), specifically the following:
153
+ - Install Ruby+Devkit (including MSYS/MINGW toolchains) through Windows Installer: https://rubyinstaller.org/downloads/
154
+ - Install the ActiveTcl Windows package from [ActiveState.com](https://activestate.com)
155
+ - Follow this instruction: "First, Ruby needs to find the tcl86t.dll and tk86t.dll shared libraries. These are located in C:\ActiveTcl\bin. Make a copy of them somewhere Ruby can find them, e.g. C:\Ruby26\bin."
156
+ - Setup environment variables TCL_LIBRARY=C:\ActiveTcl\lib\tcl8.6 & TK_LIBRARY=C:\ActiveTcl\lib\tk8.6
157
+ - Run: `gem install tk -v0.4.0`
158
+
149
159
  Afterwards, if you open `irb`, you should be able to run `require 'tk'` successfully.
150
160
 
151
161
  ## Setup
@@ -161,7 +171,7 @@ gem install glimmer-dsl-tk
161
171
 
162
172
  Add the following to `Gemfile`:
163
173
  ```
164
- gem 'glimmer-dsl-tk', '~> 0.0.37'
174
+ gem 'glimmer-dsl-tk', '~> 0.0.38'
165
175
  ```
166
176
 
167
177
  And, then run:
@@ -285,10 +295,13 @@ keyword(args) | attributes | event bindings & callbacks
285
295
  `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
286
296
  `list` | `selectmode`, `selection` | None
287
297
  `message_box(type: , message: , detail: , title: , icon: , default: , parent: )` | None | None
298
+ `menu(label: nil) (label is nil if nested under root/toplevel for menu bar)` | None | None
299
+ `menu_item(style = :command, label: , underline: )` (style also can be `:radiobutton`, `:checkbutton`, or `:separator`) | `state`, `accelerator`, `selection` & `variable` (if `:radiobutton` or `:checkbutton`) | `command`
288
300
  `notebook` | None | None
289
301
  `radiobutton` | `text`, `variable` (Boolean), `image` (optional keyword args: `subsample`, `zoom`, `from`, `to`, `shrink`, `compositingrule`), `compound` (`'center', 'top', 'bottom', 'left', 'right'`), `value` (default: `text`) | `command {}`
290
- `root` | `title`, `iconphoto`, `background`, `alpha`, `fullscreen?`, `topmost?`, `transparent?`, `stackorder`, `winfo_screendepth`, `winfo_screenvisual`, `winfo_screenwidth`, `winfo_screenheight`, `winfo_pixels('li')`, `winfo_screen`, `wm_maxsize`, `state` (`'normal', 'iconic', 'withdrawn', 'icon', 'zoomed'`) | `'DELETE_WINDOW'`, `'OPEN_WINDOW'`
302
+ `root` | `title`, `iconphoto`, `background`, `alpha`, `escapable`, `fullscreen?`, `topmost?`, `transparent?`, `stackorder`, `winfo_screendepth`, `winfo_screenvisual`, `winfo_screenwidth`, `winfo_screenheight`, `winfo_pixels('li')`, `winfo_screen`, `wm_maxsize`, `state` (`'normal', 'iconic', 'withdrawn', 'icon', 'zoomed'`) | `'DELETE_WINDOW'`, `'OPEN_WINDOW'`
291
303
  `separator` | `orient` (`'horizontal' (default) or 'vertical'`) | None
304
+ `toplevel` | `title`, `iconphoto`, `background`, `alpha`, `escapable`, `fullscreen?`, `topmost?`, `transparent?`, `stackorder`, `winfo_screendepth`, `winfo_screenvisual`, `winfo_screenwidth`, `winfo_screenheight`, `winfo_pixels('li')`, `winfo_screen`, `wm_maxsize`, `state` (`'normal', 'iconic', 'withdrawn', 'icon', 'zoomed'`) | `'DELETE_WINDOW'`
292
305
  `text` | `value`, [many more attributes](https://tcl.tk/man/tcl8.6/TkCmd/text.htm#M116) | `'modified'`, `'selection'`, `'insert_mark_moved'` (alias: `'insert_mark_move', 'InsertMarkMove', 'InsertMarkMoved'`)
293
306
 
294
307
  Options for `get_open_file` and `get_multiple_open_file` include:
@@ -456,6 +469,10 @@ Frames have a padding of `15` all around by default to produce more user-friendl
456
469
 
457
470
  When nesting `frame` under `notebook`, you can pass a `:text` keyword argument to indicate the tab title.
458
471
 
472
+ #### Menu Item Accelerator
473
+
474
+ `menu_item` `accelerator` is automatically bound to the `command` set inside `menu_item` content (no need to do manual binding as per [Tk docs](https://tkdocs.com/tutorial/menus.html)).
475
+
459
476
  #### Icon Photo
460
477
 
461
478
  The `iconphoto` attribute on `root` is set to the Glimmer icon by default if no icon photo is supplied.
@@ -2920,6 +2937,203 @@ Glimmer app:
2920
2937
 
2921
2938
  ![glimmer dsl tk screenshot sample hello separator](images/glimmer-dsl-tk-screenshot-sample-hello-separator.png)
2922
2939
 
2940
+ ### Hello, Menu Bar!
2941
+
2942
+ Glimmer code (from [samples/hello/hello_menu_bar.rb](samples/hello/hello_menu_bar.rb)):
2943
+
2944
+ ```ruby
2945
+ require 'glimmer-dsl-tk'
2946
+
2947
+ include Glimmer
2948
+
2949
+ COLORS = [:white, :red, :yellow, :green, :blue, :magenta, :gray, :black]
2950
+
2951
+ Tk::Tile::Style.theme_use "classic" # this enables setting background on label just for demo purposes
2952
+
2953
+ root { |r|
2954
+ title 'Hello, Menu Bar!'
2955
+
2956
+ @label = label {
2957
+ grid row_weight: 1, column_weight: 1
2958
+ text 'Check Out The Menu Bar Above!'
2959
+ font size: 50
2960
+ anchor 'center'
2961
+ }
2962
+
2963
+ menu {
2964
+ menu(label: 'File', underline: 0) {
2965
+ menu_item(label: 'New', underline: 0) {
2966
+ accelerator 'Command+N'
2967
+
2968
+ on('command') do
2969
+ message_box(parent: r, title: 'New', message: 'New file created.')
2970
+ end
2971
+ }
2972
+ menu_item(label: 'Open...', underline: 0) {
2973
+ accelerator 'Command+O'
2974
+
2975
+ on('command') do
2976
+ message_box(parent: r, title: 'Open', message: 'Opening File...')
2977
+ end
2978
+ }
2979
+ menu(label: 'Open Recent', underline: 5) {
2980
+ menu_item(label: 'File 1') {
2981
+ on('command') do
2982
+ message_box(parent: r, title: 'File 1', message: 'File 1 Contents')
2983
+ end
2984
+ }
2985
+ menu_item(label: 'File 2') {
2986
+ on('command') do
2987
+ message_box(parent: r, title: 'File 2', message: 'File 2 Contents')
2988
+ end
2989
+ }
2990
+ }
2991
+ menu_item(:separator)
2992
+ menu_item(label: 'Exit', underline: 1) {
2993
+ on('command') do
2994
+ exit(0)
2995
+ end
2996
+ }
2997
+ }
2998
+ menu(label: 'Edit', underline: 0) {
2999
+ menu_item(label: 'Cut', underline: 2) {
3000
+ accelerator 'Command+X'
3001
+ }
3002
+ menu_item(label: 'Copy', underline: 0) {
3003
+ accelerator 'Command+C'
3004
+ }
3005
+ menu_item(label: 'Paste', underline: 0) {
3006
+ accelerator 'Command+V'
3007
+ }
3008
+ }
3009
+ menu(label: 'Options', underline: 0) {
3010
+ menu_item(:checkbutton, label: 'Enabled', underline: 0) {
3011
+ on('command') do
3012
+ @select_one_menu.children.each { |menu_item| menu_item.state = menu_item.state == 'disabled' ? 'normal' : 'disabled' }
3013
+ @select_multiple_menu.children.each { |menu_item| menu_item.state = menu_item.state == 'disabled' ? 'normal' : 'disabled' }
3014
+ end
3015
+ }
3016
+ @select_one_menu = menu(label: 'Select One', underline: 0) {
3017
+ menu_item(:radiobutton, label: 'Option 1') {
3018
+ state 'disabled'
3019
+ }
3020
+ menu_item(:radiobutton, label: 'Option 2') {
3021
+ state 'disabled'
3022
+ }
3023
+ menu_item(:radiobutton, label: 'Option 3') {
3024
+ state 'disabled'
3025
+ }
3026
+ }
3027
+ @select_multiple_menu = menu(label: 'Select Multiple', underline: 0) {
3028
+ menu_item(:checkbutton, label: 'Option 4') {
3029
+ state 'disabled'
3030
+ }
3031
+ menu_item(:checkbutton, label: 'Option 5') {
3032
+ state 'disabled'
3033
+ }
3034
+ menu_item(:checkbutton, label: 'Option 6') {
3035
+ state 'disabled'
3036
+ }
3037
+ }
3038
+ }
3039
+ menu(label: 'Format', underline: 0) {
3040
+ menu(label: 'Background Color', underline: 0) {
3041
+ COLORS.each { |color_style|
3042
+ menu_item(:radiobutton, label: color_style.to_s.split('_').map(&:capitalize).join(' ')) {
3043
+ on('command') do
3044
+ @label.background = color_style
3045
+ end
3046
+ }
3047
+ }
3048
+ }
3049
+ menu(label: 'Foreground Color', underline: 11) {
3050
+ COLORS.each { |color_style|
3051
+ menu_item(:radiobutton, label: color_style.to_s.split('_').map(&:capitalize).join(' ')) {
3052
+ on('command') do
3053
+ @label.foreground = color_style
3054
+ end
3055
+ }
3056
+ }
3057
+ }
3058
+ }
3059
+ menu(label: 'View', underline: 0) {
3060
+ menu_item(:radiobutton, label: 'Small', underline: 0) {
3061
+ accelerator 'Command+S'
3062
+
3063
+ on('command') do
3064
+ @label.font = {size: 25}
3065
+ end
3066
+ }
3067
+ menu_item(:radiobutton, label: 'Medium', underline: 0) {
3068
+ accelerator 'Command+M'
3069
+ selection true
3070
+
3071
+ on('command') do
3072
+ @label.font = {size: 50}
3073
+ end
3074
+ }
3075
+ menu_item(:radiobutton, label: 'Large', underline: 0) {
3076
+ accelerator 'Command+L'
3077
+
3078
+ on('command') do
3079
+ @label.font = {size: 75}
3080
+ end
3081
+ }
3082
+ }
3083
+ menu(label: 'Help', underline: 0) {
3084
+ menu_item(label: 'Manual', underline: 0) {
3085
+ accelerator 'Command+Shift+M'
3086
+
3087
+ on('command') do
3088
+ message_box(parent: r, title: 'Manual', message: 'Manual Contents')
3089
+ end
3090
+ }
3091
+ menu_item(label: 'Tutorial', underline: 0) {
3092
+ accelerator 'Command+Shift+T'
3093
+
3094
+ on('command') do
3095
+ message_box(parent: r, title: 'Tutorial', message: 'Tutorial Contents')
3096
+ end
3097
+ }
3098
+ menu_item(:separator)
3099
+ menu_item(label: 'Report an Issue...', underline: 0) {
3100
+ on('command') do
3101
+ message_box(parent: r, title: 'Report an Issue', message: 'Reporting an issue...')
3102
+ end
3103
+ }
3104
+ }
3105
+ }
3106
+ }.open
3107
+ ```
3108
+
3109
+ Run with [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed:
3110
+
3111
+ ```
3112
+ ruby -r glimmer-dsl-tk -e "require 'samples/hello/hello_menu_bar'"
3113
+ ```
3114
+
3115
+ Alternatively, run from cloned project without [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed:
3116
+
3117
+ ```
3118
+ ruby -r ./lib/glimmer-dsl-tk.rb samples/hello/hello_menu_bar.rb
3119
+ ```
3120
+
3121
+ Glimmer app:
3122
+
3123
+ ![glimmer dsl tk screenshot sample hello menu-bar](images/glimmer-dsl-tk-screenshot-sample-hello-menu-bar.png)
3124
+
3125
+ ![glimmer dsl tk screenshot sample hello menu-bar](images/glimmer-dsl-tk-screenshot-sample-hello-menu-bar-file.png)
3126
+
3127
+ ![glimmer dsl tk screenshot sample hello menu-bar](images/glimmer-dsl-tk-screenshot-sample-hello-menu-bar-edit.png)
3128
+
3129
+ ![glimmer dsl tk screenshot sample hello menu-bar](images/glimmer-dsl-tk-screenshot-sample-hello-menu-bar-options.png)
3130
+
3131
+ ![glimmer dsl tk screenshot sample hello menu-bar](images/glimmer-dsl-tk-screenshot-sample-hello-menu-bar-format.png)
3132
+
3133
+ ![glimmer dsl tk screenshot sample hello menu-bar](images/glimmer-dsl-tk-screenshot-sample-hello-menu-bar-view.png)
3134
+
3135
+ ![glimmer dsl tk screenshot sample hello menu-bar](images/glimmer-dsl-tk-screenshot-sample-hello-menu-bar-help.png)
3136
+
2923
3137
  ## Applications
2924
3138
 
2925
3139
  ### Glimmer Tk Calculator
@@ -2942,6 +3156,14 @@ https://github.com/AndyObtiva/cryptopunks-gui
2942
3156
 
2943
3157
  ![CryptoPunks GUI Screenshot](https://raw.githubusercontent.com/AndyObtiva/cryptopunks-gui/master/screenshots/cryptopunks-gui-screenshot.png)
2944
3158
 
3159
+ ### Circule
3160
+
3161
+ Generate an icon of overlapping circles derived from a hash.
3162
+
3163
+ https://gitlab.com/fjc/circule
3164
+
3165
+ [Demo Video](https://gitlab.com/fjc/circule/-/raw/master/circule-demo.mp4)
3166
+
2945
3167
  ## Process
2946
3168
 
2947
3169
  [Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.37
1
+ 0.0.38
Binary file
@@ -0,0 +1,144 @@
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
+ class MenuItemProxy < WidgetProxy
27
+ ACCELERATOR_MODIFIER_EVENT_MAP = {
28
+ 'Command' => 'Command',
29
+ 'Cmd' => 'Command',
30
+ 'Meta' => 'Command',
31
+ 'Option' => 'Option',
32
+ 'Opt' => 'Option',
33
+ 'Alt' => 'Option',
34
+ 'Shift' => 'Shift',
35
+ 'Ctrl' => 'Control',
36
+ 'Control' => 'Control',
37
+ }
38
+
39
+ attr_reader :options
40
+
41
+ def initialize(underscored_widget_name, parent_proxy, args, &block)
42
+ @options = args.last.is_a?(Hash) ? args.last : {}
43
+ super
44
+ end
45
+
46
+ def accelerator=(value)
47
+ @accelerator = value
48
+ @parent_proxy.tk.entryconfigure @options[:label], accelerator: value
49
+ root_parent_proxy.bind(accelerator_event) do |event|
50
+ @command_block&.call(event)
51
+ end
52
+ end
53
+
54
+ def accelerator
55
+ @accelerator
56
+ end
57
+
58
+ def accelerator_event
59
+ accelerator_parts = @accelerator.split('+')
60
+ accelerator_parts.map do |accelerator_part|
61
+ ACCELERATOR_MODIFIER_EVENT_MAP[accelerator_part.capitalize] || accelerator_part.downcase
62
+ end.join('-')
63
+ end
64
+
65
+ def state=(value)
66
+ @state = value
67
+ @parent_proxy.tk.entryconfigure @options[:label], :state => value
68
+ end
69
+
70
+ def state
71
+ @state
72
+ end
73
+
74
+ def command_block=(proc)
75
+ @command_block = proc
76
+ @parent_proxy.tk.entryconfigure @options[:label], command: @command_block
77
+ end
78
+
79
+ def handle_listener(listener_name, &listener)
80
+ case listener_name.to_s.downcase
81
+ when 'command'
82
+ self.command_block = listener
83
+ else
84
+ super
85
+ end
86
+ end
87
+
88
+ def command?
89
+ @args.first.nil? || @args.first == :command || @args.first.is_a?(Hash)
90
+ end
91
+
92
+ def radiobutton?
93
+ @args.first == :radiobutton
94
+ end
95
+
96
+ def checkbutton?
97
+ @args.first == :radiobutton
98
+ end
99
+
100
+ def separator?
101
+ @args.first == :separator
102
+ end
103
+
104
+ def variable(auto_create: true)
105
+ if @variable.nil? && auto_create
106
+ sibling_variable = sibling_radio_menu_items.map {|mi| mi.variable(auto_create: false)}.compact.first
107
+ @variable = sibling_variable.nil? ? ::TkVariable.new : sibling_variable
108
+ else
109
+ @variable
110
+ end
111
+ @variable
112
+ end
113
+
114
+ def selection=(value)
115
+ if value
116
+ variable.value = @options[:label]
117
+ # TODO handle image case where there is no label
118
+ elsif checkbutton?
119
+ variable.value = '__unchecked__'
120
+ end
121
+ end
122
+
123
+ def selection
124
+ variable.value == @options[:label]
125
+ end
126
+
127
+ private
128
+
129
+ def sibling_radio_menu_items
130
+ @parent_proxy.children.select {|child| child.is_a?(MenuItemProxy) && radiobutton? && child != self}
131
+ end
132
+
133
+ def build_widget
134
+ @args.prepend(:command) if @args.first.is_a?(Hash)
135
+ @args.append({}) if !@args.last.is_a?(Hash)
136
+ @args.last.merge!(variable: variable, value: @options[:label]) if radiobutton? || checkbutton?
137
+ case @parent_proxy
138
+ when MenuProxy
139
+ @parent_proxy.tk.add(*@args)
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,48 @@
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
+ class MenuProxy < WidgetProxy
27
+ attr_reader :options
28
+
29
+ def initialize(underscored_widget_name, parent_proxy, args, &block)
30
+ @options = args.last.is_a?(Hash) ? args.last : {}
31
+ super
32
+ end
33
+
34
+ private
35
+
36
+ def build_widget
37
+ tk_widget_class = self.class.tk_widget_class_for(@keyword)
38
+ @tk = tk_widget_class.new(@parent_proxy.tk)
39
+ case @parent_proxy
40
+ when ToplevelProxy
41
+ @parent_proxy.tk['menu'] = @tk
42
+ when MenuProxy
43
+ @parent_proxy.tk.add(:cascade, {menu: @tk}.merge(@options))
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -47,22 +47,6 @@ module Glimmer
47
47
  Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::RootExpression.new, keyword, *args, &block)
48
48
  end
49
49
 
50
- def set_attribute(attribute, *args)
51
- case attribute.to_s
52
- when 'iconphoto'
53
- args[0..-1] = [image_argument(args)]
54
- super
55
- when 'resizable'
56
- if args.size == 1 && !args.first.is_a?(Array)
57
- self.resizable = [args.first]*2
58
- else
59
- super
60
- end
61
- else
62
- super
63
- end
64
- end
65
-
66
50
  def handle_listener(listener_name, &listener)
67
51
  case listener_name.to_s.upcase
68
52
  when 'WM_OPEN_WINDOW', 'OPEN_WINDOW'
@@ -50,6 +50,22 @@ module Glimmer
50
50
  %w[width height x y].include?(attribute.to_s) || super
51
51
  end
52
52
 
53
+ def set_attribute(attribute, *args)
54
+ case attribute.to_s
55
+ when 'iconphoto'
56
+ args[0..-1] = [image_argument(args)]
57
+ super
58
+ when 'resizable'
59
+ if args.size == 1 && !args.first.is_a?(Array)
60
+ self.resizable = [args.first]*2
61
+ else
62
+ super
63
+ end
64
+ else
65
+ super
66
+ end
67
+ end
68
+
53
69
  def width
54
70
  geometry.split(REGEX_GEOMETRY)[0].to_i
55
71
  end
@@ -77,8 +77,7 @@ module Glimmer
77
77
  @args = args
78
78
  @keyword = underscored_widget_name
79
79
  @block = block
80
- tk_widget_class = self.class.tk_widget_class_for(underscored_widget_name)
81
- @tk = tk_widget_class.new(@parent_proxy.tk, *args)
80
+ build_widget
82
81
  # a common widget initializer
83
82
  @parent_proxy.post_initialize_child(self)
84
83
  initialize_defaults
@@ -121,7 +120,7 @@ module Glimmer
121
120
  end
122
121
 
123
122
  def self.widget_exists?(underscored_widget_name)
124
- !!tk_widget_class_for(underscored_widget_name)
123
+ !!tk_widget_class_for(underscored_widget_name) || (Glimmer::Tk.constants.include?("#{underscored_widget_name.camelcase(:upper)}Proxy".to_sym) && Glimmer::Tk.const_get("#{underscored_widget_name.camelcase(:upper)}Proxy".to_sym).respond_to?(:new))
125
124
  end
126
125
 
127
126
  def tk_widget_has_attribute_setter?(attribute)
@@ -213,8 +212,8 @@ module Glimmer
213
212
  raise "#{self} cannot handle attribute #{attribute} with args #{args.inspect}"
214
213
  end
215
214
  rescue => e
216
- Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
217
- Glimmer::Config.logger.debug {e.full_message}
215
+ Glimmer::Config.logger.error {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
216
+ Glimmer::Config.logger.error {e.full_message}
218
217
  apply_style(attribute => args.first)
219
218
  end
220
219
  end
@@ -507,11 +506,16 @@ module Glimmer
507
506
 
508
507
  private
509
508
 
509
+ def build_widget
510
+ tk_widget_class = self.class.tk_widget_class_for(@keyword)
511
+ @tk = tk_widget_class.new(@parent_proxy.tk, *args)
512
+ end
513
+
510
514
  def initialize_defaults
511
515
  options = {}
512
516
  options[:sticky] = 'nsew'
513
517
  options[:column_weight] = 1 if @parent_proxy.children.count == 1
514
- grid(options) unless @tk.is_a?(::Tk::Toplevel)
518
+ grid(options) unless @tk.is_a?(::Tk::Toplevel) || @tk.is_a?(::Tk::Menu) || @tk.nil? # TODO refactor by adding a griddable? method that could be overriden by subclasses to consult for this call
515
519
  end
516
520
  end
517
521
  end
@@ -29,6 +29,9 @@ require 'puts_debuggerer' if ENV['pd'].to_s.downcase == 'true'
29
29
  require 'tk'
30
30
  require 'os'
31
31
  require 'facets/hash/symbolize_keys'
32
+ require 'facets/string/underscore'
33
+ require 'facets/string/camelcase'
34
+ require 'delegate'
32
35
 
33
36
  # Internal requires
34
37
  # require 'ext/glimmer/config'
@@ -40,3 +43,4 @@ Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
40
43
  result = false
41
44
  result ||= method == 'load_iseq'
42
45
  end
46
+ ::TkOption.add '*tearOff', 0
@@ -0,0 +1,183 @@
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-dsl-tk'
23
+
24
+ include Glimmer
25
+
26
+ COLORS = [:white, :red, :yellow, :green, :blue, :magenta, :gray, :black]
27
+
28
+ Tk::Tile::Style.theme_use "classic" # this enables setting background on label just for demo purposes
29
+
30
+ root { |r|
31
+ title 'Hello, Menu Bar!'
32
+
33
+ @label = label {
34
+ grid row_weight: 1, column_weight: 1
35
+ text 'Check Out The Menu Bar Above!'
36
+ font size: 50
37
+ anchor 'center'
38
+ }
39
+
40
+ menu {
41
+ menu(label: 'File', underline: 0) {
42
+ menu_item(label: 'New', underline: 0) {
43
+ accelerator 'Command+N'
44
+
45
+ on('command') do
46
+ message_box(parent: r, title: 'New', message: 'New file created.')
47
+ end
48
+ }
49
+ menu_item(label: 'Open...', underline: 0) {
50
+ accelerator 'Command+O'
51
+
52
+ on('command') do
53
+ message_box(parent: r, title: 'Open', message: 'Opening File...')
54
+ end
55
+ }
56
+ menu(label: 'Open Recent', underline: 5) {
57
+ menu_item(label: 'File 1') {
58
+ on('command') do
59
+ message_box(parent: r, title: 'File 1', message: 'File 1 Contents')
60
+ end
61
+ }
62
+ menu_item(label: 'File 2') {
63
+ on('command') do
64
+ message_box(parent: r, title: 'File 2', message: 'File 2 Contents')
65
+ end
66
+ }
67
+ }
68
+ menu_item(:separator)
69
+ menu_item(label: 'Exit', underline: 1) {
70
+ on('command') do
71
+ exit(0)
72
+ end
73
+ }
74
+ }
75
+ menu(label: 'Edit', underline: 0) {
76
+ menu_item(label: 'Cut', underline: 2) {
77
+ accelerator 'Command+X'
78
+ }
79
+ menu_item(label: 'Copy', underline: 0) {
80
+ accelerator 'Command+C'
81
+ }
82
+ menu_item(label: 'Paste', underline: 0) {
83
+ accelerator 'Command+V'
84
+ }
85
+ }
86
+ menu(label: 'Options', underline: 0) {
87
+ menu_item(:checkbutton, label: 'Enabled', underline: 0) {
88
+ on('command') do
89
+ @select_one_menu.children.each { |menu_item| menu_item.state = menu_item.state == 'disabled' ? 'normal' : 'disabled' }
90
+ @select_multiple_menu.children.each { |menu_item| menu_item.state = menu_item.state == 'disabled' ? 'normal' : 'disabled' }
91
+ end
92
+ }
93
+ @select_one_menu = menu(label: 'Select One', underline: 0) {
94
+ menu_item(:radiobutton, label: 'Option 1') {
95
+ state 'disabled'
96
+ }
97
+ menu_item(:radiobutton, label: 'Option 2') {
98
+ state 'disabled'
99
+ }
100
+ menu_item(:radiobutton, label: 'Option 3') {
101
+ state 'disabled'
102
+ }
103
+ }
104
+ @select_multiple_menu = menu(label: 'Select Multiple', underline: 0) {
105
+ menu_item(:checkbutton, label: 'Option 4') {
106
+ state 'disabled'
107
+ }
108
+ menu_item(:checkbutton, label: 'Option 5') {
109
+ state 'disabled'
110
+ }
111
+ menu_item(:checkbutton, label: 'Option 6') {
112
+ state 'disabled'
113
+ }
114
+ }
115
+ }
116
+ menu(label: 'Format', underline: 0) {
117
+ menu(label: 'Background Color', underline: 0) {
118
+ COLORS.each { |color_style|
119
+ menu_item(:radiobutton, label: color_style.to_s.split('_').map(&:capitalize).join(' ')) {
120
+ on('command') do
121
+ @label.background = color_style
122
+ end
123
+ }
124
+ }
125
+ }
126
+ menu(label: 'Foreground Color', underline: 11) {
127
+ COLORS.each { |color_style|
128
+ menu_item(:radiobutton, label: color_style.to_s.split('_').map(&:capitalize).join(' ')) {
129
+ on('command') do
130
+ @label.foreground = color_style
131
+ end
132
+ }
133
+ }
134
+ }
135
+ }
136
+ menu(label: 'View', underline: 0) {
137
+ menu_item(:radiobutton, label: 'Small', underline: 0) {
138
+ accelerator 'Command+S'
139
+
140
+ on('command') do
141
+ @label.font = {size: 25}
142
+ end
143
+ }
144
+ menu_item(:radiobutton, label: 'Medium', underline: 0) {
145
+ accelerator 'Command+M'
146
+ selection true
147
+
148
+ on('command') do
149
+ @label.font = {size: 50}
150
+ end
151
+ }
152
+ menu_item(:radiobutton, label: 'Large', underline: 0) {
153
+ accelerator 'Command+L'
154
+
155
+ on('command') do
156
+ @label.font = {size: 75}
157
+ end
158
+ }
159
+ }
160
+ menu(label: 'Help', underline: 0) {
161
+ menu_item(label: 'Manual', underline: 0) {
162
+ accelerator 'Command+Shift+M'
163
+
164
+ on('command') do
165
+ message_box(parent: r, title: 'Manual', message: 'Manual Contents')
166
+ end
167
+ }
168
+ menu_item(label: 'Tutorial', underline: 0) {
169
+ accelerator 'Command+Shift+T'
170
+
171
+ on('command') do
172
+ message_box(parent: r, title: 'Tutorial', message: 'Tutorial Contents')
173
+ end
174
+ }
175
+ menu_item(:separator)
176
+ menu_item(label: 'Report an Issue...', underline: 0) {
177
+ on('command') do
178
+ message_box(parent: r, title: 'Report an Issue', message: 'Reporting an issue...')
179
+ end
180
+ }
181
+ }
182
+ }
183
+ }.open
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-tk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.37
4
+ version: 0.0.38
5
5
  platform: ruby
6
6
  authors:
7
7
  - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-06 00:00:00.000000000 Z
11
+ date: 2021-11-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -235,6 +235,8 @@ files:
235
235
  - lib/glimmer/tk/frame_proxy.rb
236
236
  - lib/glimmer/tk/label_proxy.rb
237
237
  - lib/glimmer/tk/list_proxy.rb
238
+ - lib/glimmer/tk/menu_item_proxy.rb
239
+ - lib/glimmer/tk/menu_proxy.rb
238
240
  - lib/glimmer/tk/notebook_proxy.rb
239
241
  - lib/glimmer/tk/radiobutton_proxy.rb
240
242
  - lib/glimmer/tk/root_proxy.rb
@@ -258,6 +260,7 @@ files:
258
260
  - samples/hello/hello_label.rb
259
261
  - samples/hello/hello_list_multi_selection.rb
260
262
  - samples/hello/hello_list_single_selection.rb
263
+ - samples/hello/hello_menu_bar.rb
261
264
  - samples/hello/hello_message_box.rb
262
265
  - samples/hello/hello_notebook.rb
263
266
  - samples/hello/hello_radiobutton.rb