glimmer-dsl-tk 0.0.25 → 0.0.26

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: 1c940ac2487be1d7d3088f773bd34aa32f089fa58867ccdcb241886a36cc4327
4
- data.tar.gz: 814497bd3fda9e8b11b7388b4c7d35f0fc6ee966ed0d6e0e7ac010151eac3ca6
3
+ metadata.gz: 23370b36e8a1902b5115b534e8d9510ff5660d9b5daa9bf31abec321976eb485
4
+ data.tar.gz: ebb5477ba30c249375d96f81747f250ea45ea14b20bc0420b373c9a36b0d79fc
5
5
  SHA512:
6
- metadata.gz: 78c56736b536e3e284b417840f96ed0bcf8598dec29e0e09ba9a5174dce2797e1973ae1039b4715658361bf4db9b37f1862040c3e84ae9d3d4df7252f4e77ae8
7
- data.tar.gz: ca7968598a8281c48304899c4fd6bfc48e8f4647592cb41d3e6b657027f675ce974e7222dd34d7b6b0fa9caad94fc1d3f216161042d182a20c8f61f7a91f9482
6
+ metadata.gz: 7249831c8104e32a6cab14341757932acb621e8de027ff6b698e6f017361959b27174b8ecd1d9a6ea3aa428a93a4392a57ceee2d7c7f08f4513eddf95129a051
7
+ data.tar.gz: 7cafb57cb7fc10656d70d101cc32e8e0e18e6636ff34ed4c2056b8f8dbc9ec428205f96643792adaf939c2fc0789974f11be4718e6f443fca74bf4c6aee6c21a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.0.26
4
+
5
+ - Ensure spinbox data-binding updates on text key changes (without incrementing/decrementing)
6
+ - Optimize `text=` attribute setter on `text` widget when setting a text value that includes all previous text by having it not delete and reinsert, yet append instead.
7
+ - Support font attribute for arbitrary fonts with terse syntax (`font 'times 12 bold'` not `font TkFont.new('times 12 bold')`)
8
+ - Support a quicker way of tagging (instead of the two-step process of tagging with a keyword and then configuring the keyword style) (e.g. `text.tag(5.0, 6.0, :background=>'yellow', :font=>'TkFixedFont', :relief=>'raised')`)
9
+ - Support styles (via `style` keyword or with attribute defaulting to style when not available) and ability to define and apply a style in one shot
10
+ - Support `TextProxy#toggle_format` (+ `TextProxy#add_format` & `TextProxy#remove_format`) to be able to toggle a tag format option on or off for `text` widget in one shot instead of having to apply a tag and then configure a tag format
11
+ - Hello, Text! (a word processor with a toolbar having foreground color and background color)
12
+
3
13
  ## 0.0.25
4
14
 
5
15
  - Elaborate, Meta-Sample
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.25
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.26
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)
@@ -76,7 +76,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
76
76
  - [Supported Widgets](#supported-widgets)
77
77
  - [Common Attributes](#common-attributes)
78
78
  - [Common Themed Widget States](#common-themed-widget-states)
79
- - [Smart Defaults and Convensions](#smart-defaults-and-convensions)
79
+ - [Smart Defaults and Conventions](#smart-defaults-and-conventions)
80
80
  - [Grid Layout](#grid-layout)
81
81
  - [Label/Button Image](#labelbutton-image)
82
82
  - [Notebook Frame](#notebook-frame)
@@ -89,7 +89,6 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
89
89
  - [List Multi Selection Data-Binding](#list-multi-selection-data-binding)
90
90
  - [Entry Data-Binding](#entry-data-binding)
91
91
  - [Spinbox Data-Binding](#spinbox-data-binding)
92
- - [Text Data-Binding](#text-data-binding)
93
92
  - [Checkbutton Data-Binding](#checkbutton-data-binding)
94
93
  - [Radiobutton Data-Binding](#radiobutton-data-binding)
95
94
  - [Command Callback](#command-callback)
@@ -108,6 +107,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
108
107
  - [Hello, List Single Selection!](#hello-list-single-selection)
109
108
  - [Hello, List Multi Selection!](#hello-list-multi-selection)
110
109
  - [Hello, Entry!](#hello-entry)
110
+ - [Hello, Text!](#hello-text)
111
111
  - [Hello, Spinbox!](#hello-spinbox)
112
112
  - [Hello, Computed!](#hello-computed)
113
113
  - [Help](#help)
@@ -146,7 +146,7 @@ gem install glimmer-dsl-tk
146
146
 
147
147
  Add the following to `Gemfile`:
148
148
  ```
149
- gem 'glimmer-dsl-tk', '~> 0.0.25'
149
+ gem 'glimmer-dsl-tk', '~> 0.0.26'
150
150
  ```
151
151
 
152
152
  And, then run:
@@ -317,7 +317,7 @@ keyword(args) | attributes | event bindings & callbacks
317
317
  - `invalid?`
318
318
  - `hover?`
319
319
 
320
- ### Smart Defaults and Convensions
320
+ ### Smart Defaults and Conventions
321
321
 
322
322
  #### Event Bindings
323
323
 
@@ -325,7 +325,11 @@ Any events that normally can be accepted by the Tk `bind` or `protocol` methods
325
325
 
326
326
  #### Grid Layout
327
327
 
328
- `grid` layout is the default on most widgets (which support it).
328
+ `grid` layout with `sticky: 'nsew'` is the default on all widgets except toplevel widgets.
329
+
330
+ Also, any widget that is the first in a series of siblings has `column_weight` as `1` to automatically resize with window resizing by default.
331
+
332
+ To override that behavior, you may set alternative `grid` keyword args if needed (e.g. `grid sticky: '', column_weight: 0` disables the smart defaults).
329
333
 
330
334
  #### Label/Button Image
331
335
 
@@ -359,6 +363,10 @@ root {
359
363
  }.open
360
364
  ```
361
365
 
366
+ #### Root Background
367
+
368
+ `root` `background` color attribute is automatically set to `'#ececec'` on the Mac to avoid having a non-native-looking light-colored background.
369
+
362
370
  ## The Grid Geometry Manager
363
371
 
364
372
  The Grid Geometry Manager is supported via the `grid` keyword just as per the [Tk documentation](https://tkdocs.com/tutorial/grid.html), except by nesting under the widget it concerns.
@@ -377,9 +385,17 @@ Example:
377
385
  }
378
386
  ```
379
387
 
388
+ Extra convenience options may be passed to `grid` when using [Glimmer DSL for Tk](https://rubygems.org/gems/glimmer-dsl-tk):
389
+ - `min_width`: alias for `columnminsize` being called on `TkGrid.columnconfigure`
390
+ - `min_height`: alias for `rowminsize` being called on `TkGrid.rowconfigure`
391
+ - `column_weight`: alias for `columnweight` being called on `TkGrid.columnconfigure`
392
+ - `row_weight`: alias for `rowweight` being called on `TkGrid.rowconfigure`
393
+
394
+ Note also the [Grid Layout](#grid-layout) conventions (e.g. `column_weight` is automatically set to `1` for the first widget in a series of siblings to automatically have all resize when window resize)
395
+
380
396
  More details can be found in the [Hello, Computed!](#hello-computed) sample below.
381
397
 
382
- ## Bidirectional Data-Binding
398
+ ## Data-Binding
383
399
 
384
400
  Glimmer supports Shine syntax bidirectional data-binding via the `<=>` operator (read-write) and unidirectional data-binding via the `<=` operator (read-only), which takes a model and an attribute (the `bind` keyword may also be used as the old-style of data-binding).
385
401
 
@@ -409,7 +425,7 @@ This assumes a `Person` model with a `country` attribute representing their curr
409
425
 
410
426
  ```ruby
411
427
  combobox {
412
- state 'readonly'
428
+ readonly true # this applies to text editing only (item selection still triggers a write to model)
413
429
  text <=> [person, :country]
414
430
  }
415
431
  ```
@@ -570,7 +586,7 @@ More details can be found in the [Hello, Radiobutton!](#hello-radiobutton) sampl
570
586
 
571
587
  ## Command Callback
572
588
 
573
- `button` and `checkbutton` can set a `command` block to trigger when the user clicks the button/checkbutton. This may be done with the `command` keyword, passing in a block directly.
589
+ `button`, `spinbox`, `radiobutton` and `checkbutton` can set a `command` block to trigger when the user clicks the button/checkbutton. This may be done with the `command` keyword, passing in a block directly.
574
590
 
575
591
  Example:
576
592
 
@@ -584,6 +600,18 @@ Example:
584
600
  }
585
601
  ```
586
602
 
603
+ Alternatively, it can be treated as simply an event for consistency with other event bindings:
604
+
605
+ ```ruby
606
+ button {
607
+ text "Reset Selection"
608
+
609
+ on('command') do
610
+ person.reset_country
611
+ end
612
+ }
613
+ ```
614
+
587
615
  More details can be found in the [Hello, Button!](#hello-button) sample below.
588
616
 
589
617
  ## Gotchas
@@ -1390,12 +1418,12 @@ Glimmer code (from [samples/hello/hello_combobox.rb](samples/hello/hello_combobo
1390
1418
  root {
1391
1419
  title 'Hello, Combobox!'
1392
1420
 
1393
- combobox { |proxy|
1394
- state 'readonly'
1421
+ combobox {
1422
+ readonly true # this applies to text editing only (item selection still triggers a write to model)
1395
1423
  text <=> [person, :country]
1396
1424
  }
1397
1425
 
1398
- button { |proxy|
1426
+ button {
1399
1427
  text "Reset Selection"
1400
1428
  command {
1401
1429
  person.reset_country
@@ -1499,86 +1527,6 @@ Glimmer app:
1499
1527
 
1500
1528
  ![glimmer dsl tk screenshot sample hello list multi selection](images/glimmer-dsl-tk-screenshot-sample-hello-list-multi-selection.png)
1501
1529
 
1502
- ### Hello, Computed!
1503
-
1504
- Glimmer code (from [samples/hello/hello_computed.rb](samples/hello/hello_computed.rb)):
1505
-
1506
- ```ruby
1507
- # ... more code precedes
1508
- root {
1509
- title 'Hello, Computed!'
1510
-
1511
- frame {
1512
- grid column: 0, row: 0, padx: 5, pady: 5
1513
-
1514
- label {
1515
- grid column: 0, row: 0, sticky: 'w'
1516
- text 'First Name: '
1517
- }
1518
- entry {
1519
- grid column: 1, row: 0
1520
- width 15
1521
- text <=> [@contact, :first_name]
1522
- }
1523
-
1524
- label {
1525
- grid column: 0, row: 1, sticky: 'w'
1526
- text 'Last Name: '
1527
- }
1528
- entry {
1529
- grid column: 1, row: 1
1530
- width 15
1531
- text <=> [@contact, :last_name]
1532
- }
1533
-
1534
- label {
1535
- grid column: 0, row: 2, sticky: 'w'
1536
- text 'Year of Birth: '
1537
- }
1538
- entry {
1539
- grid column: 1, row: 2
1540
- width 15
1541
- text <=> [@contact, :year_of_birth]
1542
- }
1543
-
1544
- label {
1545
- grid column: 0, row: 3, sticky: 'w'
1546
- text 'Name: '
1547
- }
1548
- label {
1549
- grid column: 1, row: 3, sticky: 'w'
1550
- text <=> [@contact, :name, computed_by: [:first_name, :last_name]]
1551
- }
1552
-
1553
- label {
1554
- grid column: 0, row: 4, sticky: 'w'
1555
- text 'Age: '
1556
- }
1557
- label {
1558
- grid column: 1, row: 4, sticky: 'w'
1559
- text <=> [@contact, :age, on_write: :to_i, computed_by: [:year_of_birth]]
1560
- }
1561
- }
1562
- }.open
1563
- # ... more code follows
1564
- ```
1565
-
1566
- Run with [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed:
1567
-
1568
- ```
1569
- ruby -r glimmer-dsl-tk -e "require 'samples/hello/hello_computed'"
1570
- ```
1571
-
1572
- Alternatively, run from cloned project without [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed:
1573
-
1574
- ```
1575
- ruby -r ./lib/glimmer-dsl-tk.rb samples/hello/hello_computed.rb
1576
- ```
1577
-
1578
- Glimmer app:
1579
-
1580
- ![glimmer dsl tk screenshot sample hello computed](images/glimmer-dsl-tk-screenshot-sample-hello-computed.png)
1581
-
1582
1530
  ### Hello, Entry!
1583
1531
 
1584
1532
  Glimmer code (from [samples/hello/hello_entry.rb](samples/hello/hello_entry.rb)):
@@ -1603,7 +1551,7 @@ class HelloEntry
1603
1551
  title 'Hello, Entry!'
1604
1552
 
1605
1553
  label {
1606
- grid sticky: 'ew', column_weight: 1
1554
+ grid sticky: 'ew'
1607
1555
  text 'default entry'
1608
1556
  }
1609
1557
  entry {
@@ -1686,6 +1634,111 @@ Glimmer app:
1686
1634
  ![glimmer dsl tk screenshot sample hello entry](images/glimmer-dsl-tk-screenshot-sample-hello-entry.png)
1687
1635
  ![glimmer dsl tk screenshot sample hello entry validated](images/glimmer-dsl-tk-screenshot-sample-hello-entry-validated.png)
1688
1636
 
1637
+ ### Hello, Text!
1638
+
1639
+ [Glimmer DSL for Tk](https://rubygems.org/gems/glimmer-dsl-tk) automatically provides a `text` attribute for the `text` widget that enables updating its content simply without worrying about whether to manually insert by index, delete, or append.
1640
+
1641
+ Glimmer code (from [samples/hello/hello_text.rb](samples/hello/hello_text.rb)):
1642
+
1643
+ ```ruby
1644
+ require 'glimmer-dsl-tk'
1645
+
1646
+ class HelloText
1647
+ include Glimmer
1648
+
1649
+ COLOR_OPTIONS = %w[black purple blue green orange yellow red white].map(&:capitalize)
1650
+ FOREGROUND_PROMPT = '<select foreground>'
1651
+ BACKGROUND_PROMPT = '<select background>'
1652
+
1653
+ def initialize
1654
+ @foreground = FOREGROUND_PROMPT
1655
+ @background = BACKGROUND_PROMPT
1656
+ end
1657
+
1658
+ attr_accessor :foreground
1659
+
1660
+ def foreground_options
1661
+ [FOREGROUND_PROMPT] + COLOR_OPTIONS
1662
+ end
1663
+
1664
+ attr_accessor :background
1665
+
1666
+ def background_options
1667
+ [BACKGROUND_PROMPT] + COLOR_OPTIONS
1668
+ end
1669
+
1670
+ def launch
1671
+ root {
1672
+ title 'Hello, Text!'
1673
+
1674
+ frame {
1675
+ grid row: 0, column: 0
1676
+
1677
+ combobox {
1678
+ grid row: 0, column: 0, column_weight: 1
1679
+ readonly true
1680
+ text <=> [self, :foreground, after_write: ->(value) { @text.add_selection_format('foreground', value == FOREGROUND_PROMPT ? 'black' : value) }]
1681
+ }
1682
+
1683
+ combobox {
1684
+ grid row: 0, column: 1, column_weight: 1
1685
+ readonly true
1686
+ text <=> [self, :background, after_write: ->(value) { @text.add_selection_format('background', value == BACKGROUND_PROMPT ? 'black' : value) }]
1687
+ }
1688
+ }
1689
+
1690
+ @text = text {
1691
+ grid row: 1, column: 0, row_weight: 1
1692
+ text <<~MULTI_LINE_STRING
1693
+ According to the National Post, a heavy metal-loving high school principal in Canada will be allowed to keep her job, days after a public campaign to oust her made headlines around the globe.
1694
+
1695
+ Parents at Eden High School in St. Catharines, Ontario launched a petition to remove principal Sharon Burns after discovering she's an IRON MAIDEN fan.
1696
+
1697
+ The petition, titled "Eden High School Principal, Sharon Burns, Needs to Be Transferred Immediately!" read, "We are deeply disturbed that the principal assigned to the school blatantly showed Satanic symbols and her allegiance to Satanic practices on her public social media platforms where all the students can see them under @edenprincipal (not her personal account)."
1698
+
1699
+ More than 500 people signed the petition to transfer Burns after she posted a picture of herself flashing the "devil horns" hand sign with a doll of the MAIDEN zombie mascot Eddie behind her as well as a personalized license plate reading "IRNMADEN" and a handwritten note that reads "Eddie 666" on a car's dashboard.
1700
+
1701
+
1702
+ The number 666 is used to represent the devil, and is featured prominently in MAIDEN's artwork by the band, whose classic third album is titled "The Number Of The Beast".
1703
+
1704
+ The petition was later updated to state that the demand for her transfer is not because of her love for metal, but for "openly displaying her own handmade sign with the 666 clearly displayed on it".
1705
+
1706
+ The creator of the original petition wrote: "Sharon knows full well what she did was simply inappropriate, unnecessary and not professional but has yet to publicly admit so and is willing to allow people to believe a completely different story, making very real concerns seem petty."
1707
+
1708
+ Meanwhile, a counter-petition supporting Burns garnered over 20,000 signatures.
1709
+
1710
+ "It is ridiculous that a couple of parents judge her role as a principal only based on an Instagram post. (About liking the band IRON MAIDEN. That's it.) Eden High School is a public school. Not a Christian school," the petition titled "We need Mrs Burns" stated. "She has made Eden a safe space for so many people. She spreads nothing but love and kindness."
1711
+
1712
+ After the complaints were aired, the District School Board of Niagara spoke with Burns and the parents who launched the petition, and the issue is over as far as the board is concerned, Kim Sweeney, the board's chief communications officer, told the National Post. No disciplinary action or policy changes were needed.
1713
+
1714
+ "Our belief is that taste in music is subjective and we support that both students and staff enjoy a wide variety of genres," Sweeney said.
1715
+
1716
+ The original petition has since been removed.
1717
+ MULTI_LINE_STRING
1718
+ }
1719
+ }.open
1720
+ end
1721
+ end
1722
+
1723
+ HelloText.new.launch
1724
+ ```
1725
+
1726
+ Run with [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed:
1727
+
1728
+ ```
1729
+ ruby -r glimmer-dsl-tk -e "require 'samples/hello/hello_text'"
1730
+ ```
1731
+
1732
+ Alternatively, run from cloned project without [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed:
1733
+
1734
+ ```
1735
+ ruby -r ./lib/glimmer-dsl-tk.rb samples/hello/hello_text.rb
1736
+ ```
1737
+
1738
+ Glimmer app:
1739
+
1740
+ ![glimmer dsl tk screenshot sample hello text](images/glimmer-dsl-tk-screenshot-sample-hello-text.png)
1741
+
1689
1742
  ### Hello, Spinbox!
1690
1743
 
1691
1744
  Glimmer code (from [samples/hello/hello_spinbox.rb](samples/hello/hello_spinbox.rb)):
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.25
1
+ 0.0.26
Binary file
@@ -30,10 +30,11 @@ module Glimmer
30
30
  class LabelProxy < WidgetProxy
31
31
  include TextVariableOwner
32
32
 
33
- def set_attribute(attribute, *args)
34
- if attribute.to_s == 'font'
35
- args[0] = "tk_#{args[0]}_font".camelcase(:upper) if (args[0].is_a?(Symbol) || args[0].is_a?(String)) && args[0].to_s == args[0].to_s.downcase
36
- super
33
+ FONTS_PREDEFINED = %w[default text fixed menu heading caption small_caption icon tooltip]
34
+
35
+ def font=(value)
36
+ if (value.is_a?(Symbol) || value.is_a?(String)) && FONTS_PREDEFINED.include?(value.to_s.downcase)
37
+ @tk.font = "tk_#{value}_font".camelcase(:upper)
37
38
  else
38
39
  super
39
40
  end
@@ -59,16 +59,6 @@ module Glimmer
59
59
  when 'iconphoto'
60
60
  args[0..-1] = [image_argument(args)]
61
61
  super
62
- when 'width'
63
- @width = args.first.to_i
64
- self.geometry = "#{args.first.to_i}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
65
- when 'height'
66
- @height = args.first.to_i
67
- self.geometry = "#{@width || DEFAULT_WIDTH}x#{args.first.to_i}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
68
- when 'x'
69
- self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{args.first.to_i > 0 ? '+' : '-'}#{args.first.to_i.abs}#{y_sign}#{abs_y}"
70
- when 'y'
71
- self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{args.first.to_i > 0 ? '+' : '-'}#{args.first.to_i.abs}"
72
62
  when 'resizable'
73
63
  if args.size == 1 && !args.first.is_a?(Array)
74
64
  self.resizable = [args.first]*2
@@ -80,52 +70,38 @@ module Glimmer
80
70
  end
81
71
  end
82
72
 
83
- def get_attribute(attribute)
84
- attribute = attribute.to_s
85
- case attribute
86
- when 'width'
87
- geometry.split(REGEX_GEOMETRY)[0].to_i
88
- when 'height'
89
- geometry.split(REGEX_GEOMETRY)[1].to_i
90
- when 'x'
91
- sign_number(x_sign, geometry.split(REGEX_GEOMETRY)[2].to_i)
92
- when 'y'
93
- sign_number(y_sign, geometry.split(REGEX_GEOMETRY)[3].to_i)
94
- else
95
- super
96
- end
97
- end
98
-
99
73
  def width
100
- get_attribute(:width)
74
+ geometry.split(REGEX_GEOMETRY)[0].to_i
101
75
  end
102
76
 
103
77
  def height
104
- get_attribute(:height)
78
+ geometry.split(REGEX_GEOMETRY)[1].to_i
105
79
  end
106
80
 
107
81
  def x
108
- get_attribute(:x)
82
+ sign_number(x_sign, geometry.split(REGEX_GEOMETRY)[2].to_i)
109
83
  end
110
84
 
111
85
  def y
112
- get_attribute(:y)
86
+ sign_number(y_sign, geometry.split(REGEX_GEOMETRY)[3].to_i)
113
87
  end
114
88
 
115
89
  def width=(value)
116
- set_attribute(:width, value)
90
+ @width = value.to_i
91
+ self.geometry = "#{value.to_i}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
117
92
  end
118
93
 
119
94
  def height=(value)
120
- set_attribute(:height, value)
95
+ @height = value.to_i
96
+ self.geometry = "#{@width || DEFAULT_WIDTH}x#{value.to_i}#{x_sign}#{abs_x}#{y_sign}#{abs_y}"
121
97
  end
122
98
 
123
99
  def x=(value)
124
- set_attribute(:x, value)
100
+ self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}#{y_sign}#{abs_y}"
125
101
  end
126
102
 
127
103
  def y=(value)
128
- set_attribute(:y, value)
104
+ self.geometry = "#{@width || DEFAULT_WIDTH}x#{@height || DEFAULT_HEIGHT}#{x_sign}#{abs_x}#{value.to_i > 0 ? '+' : '-'}#{value.to_i.abs}"
129
105
  end
130
106
 
131
107
  def handle_listener(listener_name, &listener)
@@ -45,20 +45,136 @@ module Glimmer
45
45
 
46
46
  def text=(value)
47
47
  if value != @text
48
+ if @text && value.start_with?(@text)
49
+ insert('end', value[@text.size..-1])
50
+ else
51
+ delete('1.0', 'end')
52
+ insert('end', value)
53
+ end
48
54
  @text = value
49
- delete('1.0', 'end')
50
- insert('end', value)
51
55
  end
52
56
  end
53
57
 
54
- def text(value = nil)
55
- if value.nil?
56
- @text = get("1.0", 'end')
57
- else
58
- self.text = value
58
+ def text
59
+ @text = get("1.0", 'end')
60
+ end
61
+
62
+ def add_selection_format(option, value)
63
+ process_selection_ranges { |range_start, range_end| add_format(range_start, range_end, option, value) }
64
+ end
65
+
66
+ def remove_selection_format(option, value)
67
+ process_selection_ranges { |range_start, range_end| remove_format(range_start, range_end, option, value) }
68
+ end
69
+
70
+ def toggle_selection_format(option, value)
71
+ process_selection_ranges { |range_start, range_end| toggle_format(range_start, range_end, option, value) }
72
+ end
73
+
74
+ def add_selection_font_format(option, value)
75
+ process_selection_ranges { |range_start, range_end| add_font_format(range_start, range_end, option, value) }
76
+ end
77
+
78
+ def remove_selection_font_format(option, value)
79
+ process_selection_ranges { |range_start, range_end| remove_font_format(range_start, range_end, option, value) }
80
+ end
81
+
82
+ def toggle_selection_font_format(option, value)
83
+ process_selection_ranges { |range_start, range_end| toggle_font_format(range_start, range_end, option, value) }
84
+ end
85
+
86
+ def process_selection_ranges(&processor)
87
+ @tk.tag_ranges('sel').each do |region|
88
+ range_start = region.first
89
+ range_end = region.last
90
+ processor.call(range_start, range_end)
91
+ end
92
+ end
93
+
94
+ def applied_format?(region_start, region_end, option, value)
95
+ !applied_format_tags(region_start, region_end, option, value).empty?
96
+ end
97
+
98
+ def applied_format_tags(region_start, region_end, option, value)
99
+ tag_names = @tk.tag_names - ['sel']
100
+
101
+ tag_names.select do |tag_name|
102
+ @tk.tag_ranges(tag_name).any? do |range|
103
+ if range.first.to_f <= region_start.to_f && range.last.to_f >= region_end.to_f
104
+ @tk.tag_cget(tag_name, option) == value
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ def add_format(region_start, region_end, option, value)
111
+ @@tag_number = 0 unless defined?(@@tag_number)
112
+ tag = "tag#{@@tag_number += 1}"
113
+ @tk.tag_configure(tag, {option => value})
114
+ @tk.tag_add(tag, region_start, region_end)
115
+ tag
116
+ end
117
+
118
+ def remove_format(region_start, region_end, option, value)
119
+ partial_intersection_option_applied_tags = tag_names.select do |tag_name|
120
+ @tk.tag_ranges(tag_name).any? do |range|
121
+ if range.first.to_f.between?(region_start.to_f, region_end.to_f) or
122
+ range.last.to_f.between?(region_start.to_f, region_end.to_f) or
123
+ (range.first.to_f <= region_start.to_f && range.last.to_f >= region_end.to_f)
124
+ @tk.tag_cget(tag_name, option) == value
125
+ end
126
+ end
59
127
  end
128
+
129
+ partial_intersection_option_applied_tags.each do |tag_name|
130
+ @tk.tag_remove(tag_name, region_start, region_end)
131
+ end
132
+
133
+ nil
60
134
  end
61
135
 
136
+ # toggles option/value tag (removes if already applied)
137
+ def toggle_format(region_start, region_end, option, value)
138
+ if applied_format?(region_start, region_end, option, value)
139
+ remove_format(region_start, region_end, option, value)
140
+ else
141
+ add_format(region_start, region_end, option, value)
142
+ end
143
+ end
144
+
145
+ # def applied_font_format?(region_start, region_end, option, value)
146
+ # !applied_font_format_tags(region_start, region_end, option, value).empty?
147
+ # end
148
+ #
149
+ # def applied_font_format_tags(region_start, region_end, option, value)
150
+ # tag_names = @tk.tag_names - ['sel']
151
+ #
152
+ # tag_names.select do |tag_name|
153
+ # @tk.tag_ranges(tag_name).any? do |range|
154
+ # if range.first.to_f <= region_start.to_f && range.last.to_f >= region_end.to_f
155
+ # @tk.tag_cget(tag_name, option) == value
156
+ # end
157
+ # end
158
+ # end
159
+ # end
160
+ #
161
+ # def add_font_format(region_start, region_end, option, value)
162
+ # end
163
+ #
164
+ # def remove_font_format(region_start, region_end, option, value)
165
+ # end
166
+ #
167
+ ### toggles option/value tag (removes if already applied)
168
+ # def toggle_font_format(region_start, region_end, option, value)
169
+ # if applied_font_format?(region_start, region_end, option, value)
170
+ ### ensure removing from previous font combination (perhaps checking widget font too)
171
+ # remove_font_format(region_start, region_end, option, value)
172
+ # else
173
+ ### ensure adding to previous font combination (perhaps checking widget font too)
174
+ # add_font_format(region_start, region_end, option, value)
175
+ # end
176
+ # end
177
+
62
178
  private
63
179
 
64
180
  def initialize_defaults
@@ -57,7 +57,7 @@ module Glimmer
57
57
  Glimmer::Config.logger.debug e.full_message
58
58
  end
59
59
  end
60
- tk_widget_class
60
+ tk_widget_class if tk_widget_class.respond_to?(:new)
61
61
  end
62
62
  end
63
63
 
@@ -144,45 +144,55 @@ module Glimmer
144
144
  tk_widget_has_attribute_getter_setter?(attribute) or
145
145
  has_state?(attribute) or
146
146
  has_attributes_attribute?(attribute) or
147
- respond_to?(attribute_setter(attribute), args)
147
+ respond_to?(attribute_setter(attribute), args) or
148
+ respond_to?(attribute_setter(attribute), *args, super_only: true) or
149
+ respond_to?(attribute, *args, super_only: true)
148
150
  end
149
151
 
150
152
  def set_attribute(attribute, *args)
151
- widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
152
- if respond_to?(attribute, super_only: true)
153
- send(attribute, *args)
154
- elsif respond_to?(attribute_setter(attribute), super_only: true)
155
- send(attribute_setter(attribute), *args)
156
- elsif widget_custom_attribute
157
- widget_custom_attribute[:setter][:invoker].call(@tk, args)
158
- elsif tk_widget_has_attribute_setter?(attribute)
159
- unless args.size == 1 && @tk.send(attribute) == args.first
160
- if args.size == 1
161
- @tk.send(attribute_setter(attribute), *args)
153
+ begin
154
+ widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
155
+ if respond_to?(attribute_setter(attribute), super_only: true)
156
+ send(attribute_setter(attribute), *args)
157
+ elsif respond_to?(attribute, super_only: true) && self.class.instance_method(attribute).parameters.size > 0
158
+ send(attribute, *args)
159
+ elsif widget_custom_attribute
160
+ widget_custom_attribute[:setter][:invoker].call(@tk, args)
161
+ elsif tk_widget_has_attribute_setter?(attribute)
162
+ unless args.size == 1 && @tk.send(attribute) == args.first
163
+ if args.size == 1
164
+ @tk.send(attribute_setter(attribute), *args)
165
+ else
166
+ @tk.send(attribute_setter(attribute), args)
167
+ end
168
+ end
169
+ elsif tk_widget_has_attribute_getter_setter?(attribute)
170
+ @tk.send(attribute, *args)
171
+ elsif has_state?(attribute)
172
+ attribute = attribute.sub(/=$/, '')
173
+ if !!args.first
174
+ @tk.tile_state(attribute)
162
175
  else
163
- @tk.send(attribute_setter(attribute), args)
176
+ @tk.tile_state("!#{attribute}")
164
177
  end
165
- end
166
- elsif tk_widget_has_attribute_getter_setter?(attribute)
167
- @tk.send(attribute, *args)
168
- elsif has_state?(attribute)
169
- attribute = attribute.sub(/=$/, '')
170
- if !!args.first
171
- @tk.tile_state(attribute)
178
+ elsif has_attributes_attribute?(attribute)
179
+ attribute = attribute.sub(/=$/, '')
180
+ @tk.attributes(attribute, args.first)
172
181
  else
173
- @tk.tile_state("!#{attribute}")
182
+ raise "#{self} cannot handle attribute #{attribute} with args #{args.inspect}"
174
183
  end
175
- elsif has_attributes_attribute?(attribute)
176
- attribute = attribute.sub(/=$/, '')
177
- @tk.attributes(attribute, args.first)
178
- else
179
- send(attribute_setter(attribute), args)
184
+ rescue => e
185
+ Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
186
+ Glimmer::Config.logger.debug {e.full_message}
187
+ apply_style(attribute => args.first)
180
188
  end
181
189
  end
182
190
 
183
191
  def get_attribute(attribute)
184
192
  widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute.to_s]
185
- if widget_custom_attribute
193
+ if respond_to?(attribute, super_only: true)
194
+ send(attribute)
195
+ elsif widget_custom_attribute
186
196
  widget_custom_attribute[:getter][:invoker].call(@tk, args)
187
197
  elsif tk_widget_has_attribute_getter_setter?(attribute)
188
198
  @tk.send(attribute)
@@ -201,6 +211,12 @@ module Glimmer
201
211
  "#{attribute}="
202
212
  end
203
213
 
214
+ def style=(styles)
215
+ styles.each do |attribute, value|
216
+ apply_style(attribute => value)
217
+ end
218
+ end
219
+
204
220
  def grid(options = {})
205
221
  options = options.stringify_keys
206
222
  index_in_parent = @parent_proxy.children.index(self)
@@ -221,6 +237,21 @@ module Glimmer
221
237
  @tk.grid(options)
222
238
  end
223
239
 
240
+ def font=(value)
241
+ @tk.font = value.is_a?(TkFont) ? value : TkFont.new(value)
242
+ rescue => e
243
+ Glimmer::Config.logger.debug {"Failed to set attribute #{attribute} with args #{args.inspect}. Attempting to set through style instead..."}
244
+ Glimmer::Config.logger.debug {e.full_message}
245
+ apply_style({"font" => value})
246
+ end
247
+
248
+ def apply_style(options)
249
+ @@style_number = 0 unless defined?(@@style_number)
250
+ style = "style#{@@style_number}.#{@tk.class.name.split('::').last}"
251
+ ::Tk::Tile::Style.configure(style, options)
252
+ @tk.style = style
253
+ end
254
+
224
255
  def widget_custom_attribute_mapping
225
256
  # TODO consider extracting to modules/subclasses
226
257
  @widget_custom_attribute_mapping ||= {
@@ -325,6 +356,14 @@ module Glimmer
325
356
  @tk.command {
326
357
  observer.call(@tk.textvariable&.value)
327
358
  }
359
+ @tk.validate('key')
360
+ @tk.validatecommand { |validate_args|
361
+ observer.call(validate_args.value)
362
+ new_icursor = validate_args.index
363
+ new_icursor += validate_args.string.size if validate_args.action == 1
364
+ @tk.icursor = new_icursor
365
+ true
366
+ }
328
367
  end,
329
368
  },
330
369
  ::Tk::Text => {
@@ -1,3 +1,4 @@
1
+
1
2
  # Copyright (c) 2020-2021 Andy Maleh
2
3
  #
3
4
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -44,7 +45,7 @@ class HelloCombobox
44
45
  title 'Hello, Combobox!'
45
46
 
46
47
  combobox {
47
- state 'readonly'
48
+ readonly true # this applies to text editing only (item selection still triggers a write to model)
48
49
  text <=> [person, :country]
49
50
  }
50
51
 
@@ -38,7 +38,7 @@ class HelloEntry
38
38
  title 'Hello, Entry!'
39
39
 
40
40
  label {
41
- grid sticky: 'ew', column_weight: 1
41
+ grid sticky: 'ew'
42
42
  text 'default entry'
43
43
  }
44
44
  entry {
@@ -72,7 +72,7 @@ class HelloEntry
72
72
 
73
73
  ## this event kicks in just after the text variable is validated and before it is modified
74
74
  on('invalid') do |validate_args|
75
- @validated_entry_label.text = "#{validate_args.string} is not valid!"
75
+ @validated_entry_label.text = "#{validate_args.value} is not valid!"
76
76
  @validated_entry_label.foreground = 'red'
77
77
  end
78
78
 
@@ -0,0 +1,101 @@
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
+ class HelloText
25
+ include Glimmer
26
+
27
+ COLOR_OPTIONS = %w[black purple blue green orange yellow red white].map(&:capitalize)
28
+ FOREGROUND_PROMPT = '<select foreground>'
29
+ BACKGROUND_PROMPT = '<select background>'
30
+
31
+ def initialize
32
+ @foreground = FOREGROUND_PROMPT
33
+ @background = BACKGROUND_PROMPT
34
+ end
35
+
36
+ attr_accessor :foreground
37
+
38
+ def foreground_options
39
+ [FOREGROUND_PROMPT] + COLOR_OPTIONS
40
+ end
41
+
42
+ attr_accessor :background
43
+
44
+ def background_options
45
+ [BACKGROUND_PROMPT] + COLOR_OPTIONS
46
+ end
47
+
48
+ def launch
49
+ root {
50
+ title 'Hello, Text!'
51
+
52
+ frame {
53
+ grid row: 0, column: 0
54
+
55
+ combobox {
56
+ grid row: 0, column: 0, column_weight: 1
57
+ readonly true
58
+ text <=> [self, :foreground, after_write: ->(value) { @text.add_selection_format('foreground', value == FOREGROUND_PROMPT ? 'black' : value) }]
59
+ }
60
+
61
+ combobox {
62
+ grid row: 0, column: 1, column_weight: 1
63
+ readonly true
64
+ text <=> [self, :background, after_write: ->(value) { @text.add_selection_format('background', value == BACKGROUND_PROMPT ? 'black' : value) }]
65
+ }
66
+ }
67
+
68
+ @text = text {
69
+ grid row: 1, column: 0, row_weight: 1
70
+ text <<~MULTI_LINE_STRING
71
+ According to the National Post, a heavy metal-loving high school principal in Canada will be allowed to keep her job, days after a public campaign to oust her made headlines around the globe.
72
+
73
+ Parents at Eden High School in St. Catharines, Ontario launched a petition to remove principal Sharon Burns after discovering she's an IRON MAIDEN fan.
74
+
75
+ The petition, titled "Eden High School Principal, Sharon Burns, Needs to Be Transferred Immediately!" read, "We are deeply disturbed that the principal assigned to the school blatantly showed Satanic symbols and her allegiance to Satanic practices on her public social media platforms where all the students can see them under @edenprincipal (not her personal account)."
76
+
77
+ More than 500 people signed the petition to transfer Burns after she posted a picture of herself flashing the "devil horns" hand sign with a doll of the MAIDEN zombie mascot Eddie behind her as well as a personalized license plate reading "IRNMADEN" and a handwritten note that reads "Eddie 666" on a car's dashboard.
78
+
79
+
80
+ The number 666 is used to represent the devil, and is featured prominently in MAIDEN's artwork by the band, whose classic third album is titled "The Number Of The Beast".
81
+
82
+ The petition was later updated to state that the demand for her transfer is not because of her love for metal, but for "openly displaying her own handmade sign with the 666 clearly displayed on it".
83
+
84
+ The creator of the original petition wrote: "Sharon knows full well what she did was simply inappropriate, unnecessary and not professional but has yet to publicly admit so and is willing to allow people to believe a completely different story, making very real concerns seem petty."
85
+
86
+ Meanwhile, a counter-petition supporting Burns garnered over 20,000 signatures.
87
+
88
+ "It is ridiculous that a couple of parents judge her role as a principal only based on an Instagram post. (About liking the band IRON MAIDEN. That's it.) Eden High School is a public school. Not a Christian school," the petition titled "We need Mrs Burns" stated. "She has made Eden a safe space for so many people. She spreads nothing but love and kindness."
89
+
90
+ After the complaints were aired, the District School Board of Niagara spoke with Burns and the parents who launched the petition, and the issue is over as far as the board is concerned, Kim Sweeney, the board's chief communications officer, told the National Post. No disciplinary action or policy changes were needed.
91
+
92
+ "Our belief is that taste in music is subjective and we support that both students and staff enjoy a wide variety of genres," Sweeney said.
93
+
94
+ The original petition has since been removed.
95
+ MULTI_LINE_STRING
96
+ }
97
+ }.open
98
+ end
99
+ end
100
+
101
+ HelloText.new.launch
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.25
4
+ version: 0.0.26
5
5
  platform: ruby
6
6
  authors:
7
7
  - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-16 00:00:00.000000000 Z
11
+ date: 2021-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -191,7 +191,7 @@ dependencies:
191
191
  - !ruby/object:Gem::Version
192
192
  version: 0.7.0
193
193
  description: Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library) - Ruby
194
- DSL for Tcl/Tk with bidirectional/unidirectional data-binding support
194
+ DSL for Tcl/Tk with bidirectional/unidirectional data-binding support.
195
195
  email: andy.am@gmail.com
196
196
  executables:
197
197
  - girb
@@ -257,6 +257,7 @@ files:
257
257
  - samples/hello/hello_radiobutton.rb
258
258
  - samples/hello/hello_root.rb
259
259
  - samples/hello/hello_spinbox.rb
260
+ - samples/hello/hello_text.rb
260
261
  - samples/hello/hello_world.rb
261
262
  - samples/hello/images/denmark.png
262
263
  - samples/hello/images/finland.png