ratatui_ruby 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.builds/ruby-3.2.yml +1 -1
- data/.builds/ruby-3.3.yml +1 -1
- data/.builds/ruby-3.4.yml +1 -1
- data/.builds/ruby-4.0.0.yml +1 -1
- data/CHANGELOG.md +20 -0
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/lib/ratatui_ruby/event/focus_gained.rb +50 -0
- data/lib/ratatui_ruby/event/focus_lost.rb +51 -0
- data/lib/ratatui_ruby/event/key/dwim.rb +301 -0
- data/lib/ratatui_ruby/event/key/modifier.rb +2 -0
- data/lib/ratatui_ruby/event/key.rb +9 -0
- data/lib/ratatui_ruby/event/mouse.rb +33 -0
- data/lib/ratatui_ruby/event/paste.rb +25 -0
- data/lib/ratatui_ruby/event/resize.rb +65 -0
- data/lib/ratatui_ruby/version.rb +1 -1
- data/sig/ratatui_ruby/event.rbs +97 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz: '
|
|
3
|
+
metadata.gz: 65cec825c4cedc983b69cfa150cf30bfc295b1aff70c4866dc18161e97207da0
|
|
4
|
+
data.tar.gz: '085eef207a3cb0d29e0992c1885ff788867387a725925d596bba309086ce6c2d'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c3e69e5096a3d8790d6b81ec6a3fa0589565d5cbf3661f6bc78070c2ee91d29bd068fb5a31fe84e81364544e127e629f080faf6cb533d729fda58a18ed862ba1
|
|
7
|
+
data.tar.gz: 5deeb497d1c9663d7c35efee84f8d2ca8b501810772eaf57750f2d452939e0d45c7847b13d18f59943872e3c46f649141b1e9cce09fa2242f6593168c83d3922
|
data/.builds/ruby-3.2.yml
CHANGED
data/.builds/ruby-3.3.yml
CHANGED
data/.builds/ruby-3.4.yml
CHANGED
data/.builds/ruby-4.0.0.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,23 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
12
12
|
|
|
13
13
|
### Added
|
|
14
14
|
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
### Removed
|
|
20
|
+
|
|
21
|
+
## [1.3.0] - 2026-01-27
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Punctuation Name Predicates**: `Event::Key` now supports intuitive name-based predicates for symbol keys. Use `tilde?`, `slash?`, `backslash?`, `comma?`, `period?`/`dot?`, `colon?`, `semicolon?`, `question?`, `exclamation?`/`bang?`, `at?`, `hash?`/`pound?`, `dollar?`, `percent?`, `caret?`, `ampersand?`, `asterisk?`/`star?`, `underscore?`, `hyphen?`/`dash?`/`minus?`, `plus?`, `equals?`, `pipe?`/`bar?`, `lessthan?`/`lt?`, `greaterthan?`/`gt?`, bracket predicates (`lparen?`, `rparen?`, `lbracket?`, `rbracket?`, `lbrace?`, `rbrace?` and spelled-out variants like `left_parenthesis?`), and quote predicates (`backtick?`/`grave?`, `singlequote?`/`apostrophe?`, `doublequote?`, `quote?`). The `quote?` predicate matches both single and double quotes. Underscore variants (e.g., `at_sign?`, `less_than?`) are handled automatically via method normalization.
|
|
26
|
+
- **Mouse Event DWIM Predicates**: `Event::Mouse` now supports platform-neutral button predicates (`primary?`, `secondary?`, `context_menu?`, `aux?`/`auxiliary?`), wheel aliases (`wheel_up?`, `wheel_down?`, `scroll?`), movement predicates (`moved?`, `hover?`, `hovering?`, `move?`), press/release aliases (`press?`/`pressed?`, `release?`/`released?`), and a drag predicate (`dragging?`).
|
|
27
|
+
- **Resize Event DWIM Predicates**: `Event::Resize` now supports Unix signal aliases (`sigwinch?`, `winch?`, `sig_winch?`), orientation predicates (`landscape?`, `portrait?`), and VT100 terminal size predicates (`vt100?`, `at_least_vt100?`, `over_vt100?`, `cramped?`, `constrained?`).
|
|
28
|
+
- **Focus Event DWIM Predicates**: `Event::FocusGained` and `Event::FocusLost` now support symmetric predicates for intuitive focus handling. Both respond to `focus?`, `blur?`, `gained?`, `lost?`, `active?`, `inactive?`, `foreground?`, and `background?` with semantically correct boolean values.
|
|
29
|
+
- **Paste Event DWIM Predicates**: `Event::Paste` now supports content predicates (`empty?`, `blank?`, `multiline?`/`multi_line?`, `single_line?`/`singleline?`), clipboard aliases (`clipboard?`, `pasteboard?`), and a confirmation predicate (`pasted?`).
|
|
30
|
+
|
|
31
|
+
|
|
15
32
|
### Changed
|
|
16
33
|
|
|
17
34
|
### Fixed
|
|
@@ -30,6 +47,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
30
47
|
|
|
31
48
|
### Removed
|
|
32
49
|
|
|
50
|
+
|
|
51
|
+
|
|
33
52
|
## [1.1.0] - 2026-01-25
|
|
34
53
|
|
|
35
54
|
### Added
|
|
@@ -725,6 +744,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
725
744
|
- **Testing Support**: Included `RatatuiRuby::TestHelper` and RSpec integration to make testing your TUI applications possible.
|
|
726
745
|
|
|
727
746
|
[Unreleased]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/HEAD
|
|
747
|
+
[1.3.0]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.3.0
|
|
728
748
|
[1.2.0]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.2.0
|
|
729
749
|
[1.1.0]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.1.0
|
|
730
750
|
[1.0.0]: https://git.sr.ht/~kerrick/ratatui_ruby/refs/v1.0.0
|
data/ext/ratatui_ruby/Cargo.lock
CHANGED
data/ext/ratatui_ruby/Cargo.toml
CHANGED
|
@@ -70,6 +70,56 @@ module RatatuiRuby
|
|
|
70
70
|
def ==(other)
|
|
71
71
|
other.is_a?(FocusGained)
|
|
72
72
|
end
|
|
73
|
+
|
|
74
|
+
# =========================================================================
|
|
75
|
+
# DWIM Predicates
|
|
76
|
+
# =========================================================================
|
|
77
|
+
|
|
78
|
+
# Returns true. The terminal window is now in focus.
|
|
79
|
+
#
|
|
80
|
+
# event.focus? # => true
|
|
81
|
+
def focus?
|
|
82
|
+
true
|
|
83
|
+
end
|
|
84
|
+
alias focused? focus?
|
|
85
|
+
|
|
86
|
+
# Returns true. The application gained focus.
|
|
87
|
+
#
|
|
88
|
+
# event.gained? # => true
|
|
89
|
+
def gained?
|
|
90
|
+
true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Returns false. This is not a focus lost event.
|
|
94
|
+
#
|
|
95
|
+
# event.lost? # => false
|
|
96
|
+
def lost?
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns false. Blur is the opposite of focus gained.
|
|
101
|
+
#
|
|
102
|
+
# event.blur? # => false
|
|
103
|
+
def blur?
|
|
104
|
+
false
|
|
105
|
+
end
|
|
106
|
+
alias blurred? blur?
|
|
107
|
+
|
|
108
|
+
# Returns true. The application is active.
|
|
109
|
+
#
|
|
110
|
+
# event.active? # => true
|
|
111
|
+
def active?
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
alias foreground? active?
|
|
115
|
+
|
|
116
|
+
# Returns false. The application is not inactive.
|
|
117
|
+
#
|
|
118
|
+
# event.inactive? # => false
|
|
119
|
+
def inactive?
|
|
120
|
+
false
|
|
121
|
+
end
|
|
122
|
+
alias background? inactive?
|
|
73
123
|
end
|
|
74
124
|
end
|
|
75
125
|
end
|
|
@@ -71,6 +71,57 @@ module RatatuiRuby
|
|
|
71
71
|
def ==(other)
|
|
72
72
|
other.is_a?(FocusLost)
|
|
73
73
|
end
|
|
74
|
+
|
|
75
|
+
# =========================================================================
|
|
76
|
+
# DWIM Predicates
|
|
77
|
+
# =========================================================================
|
|
78
|
+
|
|
79
|
+
# Returns true. The terminal has lost focus (blur).
|
|
80
|
+
#
|
|
81
|
+
# event.blur? # => true
|
|
82
|
+
def blur?
|
|
83
|
+
true
|
|
84
|
+
end
|
|
85
|
+
alias blurred? blur?
|
|
86
|
+
|
|
87
|
+
# Returns true. The application lost focus.
|
|
88
|
+
#
|
|
89
|
+
# event.lost? # => true
|
|
90
|
+
def lost?
|
|
91
|
+
true
|
|
92
|
+
end
|
|
93
|
+
alias unfocused? lost?
|
|
94
|
+
|
|
95
|
+
# Returns false. This is not a focus gained event.
|
|
96
|
+
#
|
|
97
|
+
# event.focus? # => false
|
|
98
|
+
def focus?
|
|
99
|
+
false
|
|
100
|
+
end
|
|
101
|
+
alias focused? focus?
|
|
102
|
+
|
|
103
|
+
# Returns false. This is not a gained event.
|
|
104
|
+
#
|
|
105
|
+
# event.gained? # => false
|
|
106
|
+
def gained?
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Returns true. The application is inactive.
|
|
111
|
+
#
|
|
112
|
+
# event.inactive? # => true
|
|
113
|
+
def inactive?
|
|
114
|
+
true
|
|
115
|
+
end
|
|
116
|
+
alias background? inactive?
|
|
117
|
+
|
|
118
|
+
# Returns false. The application is not active.
|
|
119
|
+
#
|
|
120
|
+
# event.active? # => false
|
|
121
|
+
def active?
|
|
122
|
+
false
|
|
123
|
+
end
|
|
124
|
+
alias foreground? active?
|
|
74
125
|
end
|
|
75
126
|
end
|
|
76
127
|
end
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
module RatatuiRuby
|
|
9
|
+
class Event
|
|
10
|
+
class Key < Event
|
|
11
|
+
# DWIM predicates for common key patterns.
|
|
12
|
+
#
|
|
13
|
+
# These predicates anticipate what developers intuitively try. Space bars,
|
|
14
|
+
# character categories, Unix signals, and Vim-style navigation.
|
|
15
|
+
module Dwim
|
|
16
|
+
# Returns true if the key is a space character.
|
|
17
|
+
#
|
|
18
|
+
# event.space? # => true for " "
|
|
19
|
+
def space?
|
|
20
|
+
@code == " " && @modifiers.empty?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
alias spacebar? space?
|
|
24
|
+
|
|
25
|
+
# Returns true if the key is Enter. Alias for carriage return.
|
|
26
|
+
#
|
|
27
|
+
# event.cr? # => true for enter
|
|
28
|
+
def cr?
|
|
29
|
+
@code == "enter" && @modifiers.empty?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
alias carriagereturn? cr?
|
|
33
|
+
alias linefeed? cr?
|
|
34
|
+
alias newline? cr?
|
|
35
|
+
alias lf? cr?
|
|
36
|
+
|
|
37
|
+
# Returns true if the key is a single letter (a-z, A-Z).
|
|
38
|
+
#
|
|
39
|
+
# Event::Key.new(code: "a").letter? # => true
|
|
40
|
+
def letter?
|
|
41
|
+
@code.length == 1 && @code.match?(/\A[A-Za-z]\z/)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns true if the key is a digit (0-9).
|
|
45
|
+
#
|
|
46
|
+
# Event::Key.new(code: "5").digit? # => true
|
|
47
|
+
def digit?
|
|
48
|
+
@code.length == 1 && @code.match?(/\A[0-9]\z/)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns true if the key is alphanumeric.
|
|
52
|
+
#
|
|
53
|
+
# Event::Key.new(code: "a").alphanumeric? # => true
|
|
54
|
+
def alphanumeric?
|
|
55
|
+
letter? || digit?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns true if the key is punctuation.
|
|
59
|
+
#
|
|
60
|
+
# Event::Key.new(code: "@", modifiers: ["shift"]).punctuation? # => true
|
|
61
|
+
def punctuation?
|
|
62
|
+
return false unless @code.length == 1
|
|
63
|
+
!letter? && !digit? && !whitespace?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns true if the key is whitespace (space, enter, tab).
|
|
67
|
+
#
|
|
68
|
+
# Event::Key.new(code: " ").whitespace? # => true
|
|
69
|
+
def whitespace?
|
|
70
|
+
@code == " " || @code == "enter" || @code == "tab"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Returns true for interrupt (Ctrl+C).
|
|
74
|
+
#
|
|
75
|
+
# event.interrupt? # => true for Ctrl+C
|
|
76
|
+
def interrupt?
|
|
77
|
+
@code == "c" && @modifiers == ["ctrl"]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns true for end-of-file (Ctrl+D).
|
|
81
|
+
#
|
|
82
|
+
# event.eof? # => true for Ctrl+D
|
|
83
|
+
def eof?
|
|
84
|
+
@code == "d" && @modifiers == ["ctrl"]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns true for cancel (Esc or Ctrl+C).
|
|
88
|
+
#
|
|
89
|
+
# event.cancel? # => true for Esc or Ctrl+C
|
|
90
|
+
def cancel?
|
|
91
|
+
(@code == "esc" && @modifiers.empty?) || interrupt?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns true for SIGINT (Ctrl+C).
|
|
95
|
+
#
|
|
96
|
+
# event.sigint? # => true for Ctrl+C
|
|
97
|
+
def sigint?
|
|
98
|
+
interrupt?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
alias int? sigint?
|
|
102
|
+
|
|
103
|
+
# Returns true for SIGTSTP (Ctrl+Z) - suspend/stop.
|
|
104
|
+
#
|
|
105
|
+
# event.suspend? # => true for Ctrl+Z
|
|
106
|
+
def suspend?
|
|
107
|
+
@code == "z" && @modifiers == ["ctrl"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
alias sigtstp? suspend?
|
|
111
|
+
alias tstp? suspend?
|
|
112
|
+
|
|
113
|
+
# Returns true for SIGQUIT (Ctrl+\).
|
|
114
|
+
#
|
|
115
|
+
# event.quit? # => true for Ctrl+\
|
|
116
|
+
def quit?
|
|
117
|
+
@code == "\\" && @modifiers == ["ctrl"]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
alias sigquit? quit?
|
|
121
|
+
|
|
122
|
+
NAVIGATION_KEYS = %w[up down left right home end page_up page_down].freeze # :nodoc:
|
|
123
|
+
|
|
124
|
+
# Returns true if key is a navigation key.
|
|
125
|
+
#
|
|
126
|
+
# Event::Key.new(code: "up").navigation? # => true
|
|
127
|
+
def navigation?
|
|
128
|
+
NAVIGATION_KEYS.include?(@code) && @modifiers.empty?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
ARROW_KEYS = %w[up down left right].freeze # :nodoc:
|
|
132
|
+
|
|
133
|
+
# Returns true if key is an arrow key.
|
|
134
|
+
#
|
|
135
|
+
# Event::Key.new(code: "up").arrow? # => true
|
|
136
|
+
def arrow?
|
|
137
|
+
ARROW_KEYS.include?(@code) && @modifiers.empty?
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
VIM_MOVEMENT_KEYS = %w[h j k l w b g G].freeze # :nodoc:
|
|
141
|
+
|
|
142
|
+
# Returns true if key is a Vim movement key.
|
|
143
|
+
#
|
|
144
|
+
# Event::Key.new(code: "j").vim? # => true
|
|
145
|
+
def vim?
|
|
146
|
+
return true if VIM_MOVEMENT_KEYS.include?(@code) && @modifiers.empty?
|
|
147
|
+
@code == "G" && @modifiers == ["shift"]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Returns true for Vim left (h).
|
|
151
|
+
def vim_left?
|
|
152
|
+
@code == "h" && @modifiers.empty?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Returns true for Vim down (j).
|
|
156
|
+
def vim_down?
|
|
157
|
+
@code == "j" && @modifiers.empty?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Returns true for Vim up (k).
|
|
161
|
+
def vim_up?
|
|
162
|
+
@code == "k" && @modifiers.empty?
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Returns true for Vim right (l).
|
|
166
|
+
def vim_right?
|
|
167
|
+
@code == "l" && @modifiers.empty?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Returns true for Vim word forward (w).
|
|
171
|
+
def vim_word_forward?
|
|
172
|
+
@code == "w" && @modifiers.empty?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Returns true for Vim word backward (b).
|
|
176
|
+
def vim_word_backward?
|
|
177
|
+
@code == "b" && @modifiers.empty?
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Returns true for Vim go to top (gg pattern, here just g).
|
|
181
|
+
def vim_top?
|
|
182
|
+
@code == "g" && @modifiers.empty?
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns true for Vim go to bottom (G).
|
|
186
|
+
def vim_bottom?
|
|
187
|
+
@code == "G" && @modifiers == ["shift"]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Punctuation name predicates - generated at load time for performance.
|
|
191
|
+
# Maps intuitive names to their symbol characters.
|
|
192
|
+
# :nodoc:
|
|
193
|
+
PUNCTUATION_NAMES = {
|
|
194
|
+
# Navigation shortcuts (the original use case!)
|
|
195
|
+
tilde: "~",
|
|
196
|
+
slash: "/",
|
|
197
|
+
forwardslash: "/",
|
|
198
|
+
backslash: "\\",
|
|
199
|
+
|
|
200
|
+
# Common punctuation
|
|
201
|
+
comma: ",",
|
|
202
|
+
period: ".",
|
|
203
|
+
dot: ".",
|
|
204
|
+
colon: ":",
|
|
205
|
+
semicolon: ";",
|
|
206
|
+
|
|
207
|
+
# Question and exclamation
|
|
208
|
+
question: "?",
|
|
209
|
+
questionmark: "?",
|
|
210
|
+
exclamation: "!",
|
|
211
|
+
exclamationmark: "!",
|
|
212
|
+
exclamationpoint: "!",
|
|
213
|
+
bang: "!",
|
|
214
|
+
|
|
215
|
+
# Programming symbols
|
|
216
|
+
at: "@",
|
|
217
|
+
atsign: "@",
|
|
218
|
+
hash: "#",
|
|
219
|
+
pound: "#",
|
|
220
|
+
numbersign: "#",
|
|
221
|
+
dollar: "$",
|
|
222
|
+
dollarsign: "$",
|
|
223
|
+
percent: "%",
|
|
224
|
+
caret: "^",
|
|
225
|
+
circumflex: "^",
|
|
226
|
+
ampersand: "&",
|
|
227
|
+
asterisk: "*",
|
|
228
|
+
star: "*",
|
|
229
|
+
|
|
230
|
+
# Arithmetic and comparison
|
|
231
|
+
underscore: "_",
|
|
232
|
+
hyphen: "-",
|
|
233
|
+
dash: "-",
|
|
234
|
+
minus: "-",
|
|
235
|
+
plus: "+",
|
|
236
|
+
equals: "=",
|
|
237
|
+
equalsign: "=",
|
|
238
|
+
pipe: "|",
|
|
239
|
+
bar: "|",
|
|
240
|
+
lessthan: "<",
|
|
241
|
+
lt: "<",
|
|
242
|
+
greaterthan: ">",
|
|
243
|
+
gt: ">",
|
|
244
|
+
|
|
245
|
+
# Brackets and parens
|
|
246
|
+
lparen: "(",
|
|
247
|
+
leftparen: "(",
|
|
248
|
+
openparen: "(",
|
|
249
|
+
leftparenthesis: "(",
|
|
250
|
+
openparenthesis: "(",
|
|
251
|
+
rparen: ")",
|
|
252
|
+
rightparen: ")",
|
|
253
|
+
closeparen: ")",
|
|
254
|
+
rightparenthesis: ")",
|
|
255
|
+
closeparenthesis: ")",
|
|
256
|
+
lbracket: "[",
|
|
257
|
+
leftbracket: "[",
|
|
258
|
+
openbracket: "[",
|
|
259
|
+
leftsquarebracket: "[",
|
|
260
|
+
opensquarebracket: "[",
|
|
261
|
+
rbracket: "]",
|
|
262
|
+
rightbracket: "]",
|
|
263
|
+
closebracket: "]",
|
|
264
|
+
rightsquarebracket: "]",
|
|
265
|
+
closesquarebracket: "]",
|
|
266
|
+
lbrace: "{",
|
|
267
|
+
leftbrace: "{",
|
|
268
|
+
openbrace: "{",
|
|
269
|
+
leftcurlybrace: "{",
|
|
270
|
+
opencurlybrace: "{",
|
|
271
|
+
rbrace: "}",
|
|
272
|
+
rightbrace: "}",
|
|
273
|
+
closebrace: "}",
|
|
274
|
+
rightcurlybrace: "}",
|
|
275
|
+
closecurlybrace: "}",
|
|
276
|
+
|
|
277
|
+
# Quotes
|
|
278
|
+
backtick: "`",
|
|
279
|
+
grave: "`",
|
|
280
|
+
singlequote: "'",
|
|
281
|
+
apostrophe: "'",
|
|
282
|
+
doublequote: "\"",
|
|
283
|
+
}.freeze
|
|
284
|
+
|
|
285
|
+
# Generate predicate methods at load time (faster than method_missing)
|
|
286
|
+
PUNCTUATION_NAMES.each do |name, char|
|
|
287
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
288
|
+
def #{name}?
|
|
289
|
+
@code == #{char.inspect}
|
|
290
|
+
end
|
|
291
|
+
RUBY
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# quote? matches both single and double quotes
|
|
295
|
+
def quote?
|
|
296
|
+
@code == "'" || @code == "\""
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#++
|
|
7
7
|
|
|
8
8
|
require_relative "key/character"
|
|
9
|
+
require_relative "key/dwim"
|
|
9
10
|
require_relative "key/media"
|
|
10
11
|
require_relative "key/modifier"
|
|
11
12
|
require_relative "key/navigation"
|
|
@@ -88,6 +89,7 @@ module RatatuiRuby
|
|
|
88
89
|
# These keys will not work in Terminal.app, iTerm2, or GNOME Terminal.
|
|
89
90
|
class Key < Event
|
|
90
91
|
include Character
|
|
92
|
+
include Dwim
|
|
91
93
|
include Media
|
|
92
94
|
include Modifier
|
|
93
95
|
include Navigation
|
|
@@ -435,6 +437,13 @@ module RatatuiRuby
|
|
|
435
437
|
normalized_code = @code.delete("_")
|
|
436
438
|
return true if normalized_predicate == normalized_code && @modifiers.empty?
|
|
437
439
|
|
|
440
|
+
# DWIM: Underscore variants delegate to existing methods
|
|
441
|
+
# space_bar? → spacebar? → space?, sig_int? → sigint?
|
|
442
|
+
normalized_method = :"#{normalized_predicate}?"
|
|
443
|
+
if normalized_method != name && respond_to?(normalized_method)
|
|
444
|
+
return public_send(normalized_method)
|
|
445
|
+
end
|
|
446
|
+
|
|
438
447
|
false
|
|
439
448
|
else
|
|
440
449
|
super
|
|
@@ -253,6 +253,39 @@ module RatatuiRuby
|
|
|
253
253
|
else false
|
|
254
254
|
end
|
|
255
255
|
end
|
|
256
|
+
|
|
257
|
+
alias wheel_up? scroll_up?
|
|
258
|
+
alias wheel_down? scroll_down?
|
|
259
|
+
|
|
260
|
+
# Returns true for any scroll event.
|
|
261
|
+
#
|
|
262
|
+
# event.scroll? # => true for scroll_up or scroll_down
|
|
263
|
+
def scroll?
|
|
264
|
+
scroll_up? || scroll_down?
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
alias primary? left?
|
|
268
|
+
alias secondary? right?
|
|
269
|
+
alias context_menu? right?
|
|
270
|
+
alias aux? middle?
|
|
271
|
+
alias auxiliary? aux?
|
|
272
|
+
|
|
273
|
+
# Returns true for mouse movement without button press.
|
|
274
|
+
#
|
|
275
|
+
# event.moved? # => true for moved (no button)
|
|
276
|
+
def moved?
|
|
277
|
+
@kind == "moved"
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
alias hover? moved?
|
|
281
|
+
alias hovering? moved?
|
|
282
|
+
alias move? moved?
|
|
283
|
+
alias dragging? drag?
|
|
284
|
+
|
|
285
|
+
alias release? up?
|
|
286
|
+
alias released? up?
|
|
287
|
+
alias press? down?
|
|
288
|
+
alias pressed? down?
|
|
256
289
|
end
|
|
257
290
|
end
|
|
258
291
|
end
|
|
@@ -67,6 +67,9 @@ module RatatuiRuby
|
|
|
67
67
|
def paste?
|
|
68
68
|
true
|
|
69
69
|
end
|
|
70
|
+
alias clipboard? paste?
|
|
71
|
+
alias pasteboard? paste?
|
|
72
|
+
alias pasted? paste?
|
|
70
73
|
|
|
71
74
|
# Creates a new Paste event.
|
|
72
75
|
#
|
|
@@ -100,6 +103,28 @@ module RatatuiRuby
|
|
|
100
103
|
return false unless other.is_a?(Paste)
|
|
101
104
|
content == other.content
|
|
102
105
|
end
|
|
106
|
+
|
|
107
|
+
# Returns true if the pasted content is empty.
|
|
108
|
+
def empty?
|
|
109
|
+
@content.empty?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Returns true if the pasted content is empty or whitespace-only.
|
|
113
|
+
def blank?
|
|
114
|
+
@content.strip.empty?
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Returns true if the pasted content spans multiple lines.
|
|
118
|
+
def multiline?
|
|
119
|
+
@content.include?("\n")
|
|
120
|
+
end
|
|
121
|
+
alias multi_line? multiline?
|
|
122
|
+
|
|
123
|
+
# Returns true if the pasted content is a single line.
|
|
124
|
+
def single_line?
|
|
125
|
+
!multiline?
|
|
126
|
+
end
|
|
127
|
+
alias singleline? single_line?
|
|
103
128
|
end
|
|
104
129
|
end
|
|
105
130
|
end
|
|
@@ -151,6 +151,71 @@ module RatatuiRuby
|
|
|
151
151
|
else false
|
|
152
152
|
end
|
|
153
153
|
end
|
|
154
|
+
|
|
155
|
+
# Returns true. Unix <tt>SIGWINCH</tt> triggers terminal resize.
|
|
156
|
+
#
|
|
157
|
+
# event.sigwinch? # => true
|
|
158
|
+
def sigwinch?
|
|
159
|
+
true
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
alias winch? sigwinch?
|
|
163
|
+
alias sig_winch? sigwinch?
|
|
164
|
+
|
|
165
|
+
alias terminal_resize? resize?
|
|
166
|
+
alias window_resize? resize?
|
|
167
|
+
alias window_change? resize?
|
|
168
|
+
alias viewport_resize? resize?
|
|
169
|
+
alias viewport_change? resize?
|
|
170
|
+
alias size_change? resize?
|
|
171
|
+
alias resized? resize?
|
|
172
|
+
|
|
173
|
+
VT100_WIDTH = 80 # :nodoc:
|
|
174
|
+
VT100_HEIGHT = 24 # :nodoc:
|
|
175
|
+
|
|
176
|
+
# Returns true if width exceeds height.
|
|
177
|
+
#
|
|
178
|
+
# Event::Resize.new(width: 120, height: 24).landscape? # => true
|
|
179
|
+
def landscape?
|
|
180
|
+
@width > @height
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Returns true if height exceeds or equals width.
|
|
184
|
+
#
|
|
185
|
+
# Event::Resize.new(width: 40, height: 80).portrait? # => true
|
|
186
|
+
def portrait?
|
|
187
|
+
@height >= @width
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Returns true if dimensions are exactly 80x24.
|
|
191
|
+
#
|
|
192
|
+
# Event::Resize.new(width: 80, height: 24).vt100? # => true
|
|
193
|
+
def vt100?
|
|
194
|
+
@width == VT100_WIDTH && @height == VT100_HEIGHT
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Returns true if both dimensions meet or exceed 80x24.
|
|
198
|
+
#
|
|
199
|
+
# Event::Resize.new(width: 120, height: 40).at_least_vt100? # => true
|
|
200
|
+
def at_least_vt100?
|
|
201
|
+
@width >= VT100_WIDTH && @height >= VT100_HEIGHT
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Returns true if both dimensions exceed 80x24.
|
|
205
|
+
#
|
|
206
|
+
# Event::Resize.new(width: 81, height: 25).over_vt100? # => true
|
|
207
|
+
def over_vt100?
|
|
208
|
+
@width > VT100_WIDTH && @height > VT100_HEIGHT
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Returns true if either dimension falls below VT100 standard.
|
|
212
|
+
#
|
|
213
|
+
# Event::Resize.new(width: 60, height: 24).cramped? # => true
|
|
214
|
+
def cramped?
|
|
215
|
+
@width < VT100_WIDTH || @height < VT100_HEIGHT
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
alias constrained? cramped?
|
|
154
219
|
end
|
|
155
220
|
end
|
|
156
221
|
end
|
data/lib/ratatui_ruby/version.rb
CHANGED
data/sig/ratatui_ruby/event.rbs
CHANGED
|
@@ -67,11 +67,46 @@ module RatatuiRuby
|
|
|
67
67
|
private def match_system_dwim?: (String key_name, Symbol key_sym) -> bool
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
# DWIM predicates for common key patterns
|
|
71
|
+
module Dwim
|
|
72
|
+
NAVIGATION_KEYS: Array[String]
|
|
73
|
+
ARROW_KEYS: Array[String]
|
|
74
|
+
VIM_MOVEMENT_KEYS: Array[String]
|
|
75
|
+
PUNCTUATION_NAMES: Hash[Symbol, String]
|
|
76
|
+
|
|
77
|
+
def space?: () -> bool
|
|
78
|
+
def cr?: () -> bool
|
|
79
|
+
def letter?: () -> bool
|
|
80
|
+
def digit?: () -> bool
|
|
81
|
+
def alphanumeric?: () -> bool
|
|
82
|
+
def punctuation?: () -> bool
|
|
83
|
+
def whitespace?: () -> bool
|
|
84
|
+
def interrupt?: () -> bool
|
|
85
|
+
def eof?: () -> bool
|
|
86
|
+
def cancel?: () -> bool
|
|
87
|
+
def sigint?: () -> bool
|
|
88
|
+
def suspend?: () -> bool
|
|
89
|
+
def quit?: () -> bool
|
|
90
|
+
def navigation?: () -> bool
|
|
91
|
+
def arrow?: () -> bool
|
|
92
|
+
def vim?: () -> bool
|
|
93
|
+
def vim_left?: () -> bool
|
|
94
|
+
def vim_down?: () -> bool
|
|
95
|
+
def vim_up?: () -> bool
|
|
96
|
+
def vim_right?: () -> bool
|
|
97
|
+
def vim_word_forward?: () -> bool
|
|
98
|
+
def vim_word_backward?: () -> bool
|
|
99
|
+
def vim_top?: () -> bool
|
|
100
|
+
def vim_bottom?: () -> bool
|
|
101
|
+
def quote?: () -> bool
|
|
102
|
+
end
|
|
103
|
+
|
|
70
104
|
include Character
|
|
71
105
|
include Modifier
|
|
72
106
|
include Media
|
|
73
107
|
include Navigation
|
|
74
108
|
include System
|
|
109
|
+
include Dwim
|
|
75
110
|
|
|
76
111
|
attr_reader code: String
|
|
77
112
|
attr_reader modifiers: Array[String]
|
|
@@ -114,6 +149,23 @@ module RatatuiRuby
|
|
|
114
149
|
def left?: () -> bool
|
|
115
150
|
def right?: () -> bool
|
|
116
151
|
def middle?: () -> bool
|
|
152
|
+
def scroll?: () -> bool
|
|
153
|
+
def moved?: () -> bool
|
|
154
|
+
def hover?: () -> bool
|
|
155
|
+
def hovering?: () -> bool
|
|
156
|
+
def move?: () -> bool
|
|
157
|
+
def primary?: () -> bool
|
|
158
|
+
def secondary?: () -> bool
|
|
159
|
+
def context_menu?: () -> bool
|
|
160
|
+
def aux?: () -> bool
|
|
161
|
+
def auxiliary?: () -> bool
|
|
162
|
+
def wheel_up?: () -> bool
|
|
163
|
+
def wheel_down?: () -> bool
|
|
164
|
+
def release?: () -> bool
|
|
165
|
+
def released?: () -> bool
|
|
166
|
+
def press?: () -> bool
|
|
167
|
+
def pressed?: () -> bool
|
|
168
|
+
def dragging?: () -> bool
|
|
117
169
|
def to_sym: () -> Symbol
|
|
118
170
|
def ==: (top other) -> bool
|
|
119
171
|
def deconstruct_keys: (Array[Symbol]?) -> { type: :mouse, kind: String, x: Integer, y: Integer, button: String, modifiers: Array[String] }
|
|
@@ -127,6 +179,20 @@ module RatatuiRuby
|
|
|
127
179
|
def to_sym: () -> Symbol
|
|
128
180
|
def ==: (top other) -> bool
|
|
129
181
|
def deconstruct_keys: (Array[Symbol]?) -> { type: :resize, width: Integer, height: Integer }
|
|
182
|
+
|
|
183
|
+
# DWIM predicates
|
|
184
|
+
VT100_WIDTH: Integer
|
|
185
|
+
VT100_HEIGHT: Integer
|
|
186
|
+
def sigwinch?: () -> bool
|
|
187
|
+
def winch?: () -> bool
|
|
188
|
+
def sig_winch?: () -> bool
|
|
189
|
+
def landscape?: () -> bool
|
|
190
|
+
def portrait?: () -> bool
|
|
191
|
+
def vt100?: () -> bool
|
|
192
|
+
def at_least_vt100?: () -> bool
|
|
193
|
+
def over_vt100?: () -> bool
|
|
194
|
+
def cramped?: () -> bool
|
|
195
|
+
def constrained?: () -> bool
|
|
130
196
|
end
|
|
131
197
|
|
|
132
198
|
class Paste < Event
|
|
@@ -134,14 +200,45 @@ module RatatuiRuby
|
|
|
134
200
|
|
|
135
201
|
def initialize: (content: String) -> void
|
|
136
202
|
def deconstruct_keys: (Array[Symbol]?) -> { type: :paste, content: String }
|
|
203
|
+
|
|
204
|
+
# DWIM predicates
|
|
205
|
+
def clipboard?: () -> bool
|
|
206
|
+
def pasteboard?: () -> bool
|
|
207
|
+
def pasted?: () -> bool
|
|
208
|
+
def empty?: () -> bool
|
|
209
|
+
def blank?: () -> bool
|
|
210
|
+
def multiline?: () -> bool
|
|
211
|
+
def multi_line?: () -> bool
|
|
212
|
+
def single_line?: () -> bool
|
|
213
|
+
def singleline?: () -> bool
|
|
137
214
|
end
|
|
138
215
|
|
|
139
216
|
class FocusGained < Event
|
|
140
217
|
def deconstruct_keys: (Array[Symbol]?) -> { type: :focus_gained }
|
|
218
|
+
|
|
219
|
+
# DWIM predicates
|
|
220
|
+
def focus?: () -> bool
|
|
221
|
+
def gained?: () -> bool
|
|
222
|
+
def lost?: () -> bool
|
|
223
|
+
def blur?: () -> bool
|
|
224
|
+
def active?: () -> bool
|
|
225
|
+
def inactive?: () -> bool
|
|
226
|
+
def foreground?: () -> bool
|
|
227
|
+
def background?: () -> bool
|
|
141
228
|
end
|
|
142
229
|
|
|
143
230
|
class FocusLost < Event
|
|
144
231
|
def deconstruct_keys: (Array[Symbol]?) -> { type: :focus_lost }
|
|
232
|
+
|
|
233
|
+
# DWIM predicates
|
|
234
|
+
def focus?: () -> bool
|
|
235
|
+
def gained?: () -> bool
|
|
236
|
+
def lost?: () -> bool
|
|
237
|
+
def blur?: () -> bool
|
|
238
|
+
def active?: () -> bool
|
|
239
|
+
def inactive?: () -> bool
|
|
240
|
+
def foreground?: () -> bool
|
|
241
|
+
def background?: () -> bool
|
|
145
242
|
end
|
|
146
243
|
|
|
147
244
|
class None < Event
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ratatui_ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kerrick Long
|
|
@@ -660,6 +660,7 @@ files:
|
|
|
660
660
|
- lib/ratatui_ruby/event/focus_lost.rb
|
|
661
661
|
- lib/ratatui_ruby/event/key.rb
|
|
662
662
|
- lib/ratatui_ruby/event/key/character.rb
|
|
663
|
+
- lib/ratatui_ruby/event/key/dwim.rb
|
|
663
664
|
- lib/ratatui_ruby/event/key/media.rb
|
|
664
665
|
- lib/ratatui_ruby/event/key/modifier.rb
|
|
665
666
|
- lib/ratatui_ruby/event/key/navigation.rb
|