From ef00a960a30cb8fd29d7c18a6775d4af7e9d4083 Mon Sep 17 00:00:00 2001 From: Marc Heiligers Date: Sat, 15 Feb 2025 11:47:34 -0700 Subject: [PATCH 1/3] Make @text_keys available in handle_keyboard and write up docs on special keyboard handling --- CHANGELOG.md | 5 +++++ README.md | 25 +++++++++++++++++++++++++ lib/keyboard.rb | 2 ++ lib/multiline.rb | 5 ++--- lib/text.rb | 6 ++---- metadata/game_metadata.txt | 2 +- 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37e616f..c101de5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# v0.0.23 - 16 February 2025 + +* Made `@text_keys` instance variable instead of local +* Wrote up notes on special keyboard handling with filtering example + # v0.0.22 - 9 February 2025 * Fixed a bug when using `max_length` where `selection_end` (and `selection_start`) where incorrectly set (thanks to @TheCire for reporting in Discord). diff --git a/README.md b/README.md index 18391d3..b73db61 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,31 @@ The argument list below will list `prompt_color` but not the individual `prompt_ * `#focussed?` - Returns true if the input is focussed, false otherwise * `#value_changed?` - Returns true if the input value changed in the last tick, false otherwise +## Special keyboard handling + +There may be cases where you want to do some special keyboard handling, like filtering out certain characters. In order to do this, create a subclass of `Input::Text` or `Input::Multiline` and override the `handle_keyboard` method, calling `super` when your special handling is done. The following instance variables are available: + +* `@meta` - true if the Meta key is down (the Command key on a Mac, or Windows key on Windows) +* `@alt` - true if the Alt key is down +* `@shift` - true if the Shift key is down or `shift_lock` is set +* `@ctrl` - true if the Control key is down +* `@down_keys` - an `Array` of `Symbol`s of keys that are down excluding the special keys above +* `@text_keys` - an `Array` of printable characters that has been typed this tick + +### Example: filtering + +``` ruby +class FilenameInput < Input::Text + ALLOWED_CHARS = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a + ['_', '-', '.'] + + def handle_keyboard + @text_keys.select! { |key| ALLOWED_CHARS.include?(key) } + + super + end +end +``` + ## Console replacement This library includes the ability to replace the default DragonRuby Console (`~`). Simply call `Input.replace_console!` once to enable this. diff --git a/lib/keyboard.rb b/lib/keyboard.rb index 4e2254d..cba4c5a 100644 --- a/lib/keyboard.rb +++ b/lib/keyboard.rb @@ -31,6 +31,8 @@ def prepare_special_keys # rubocop:disable Metrics/AbcSize, Metrics/MethodLength @alt = (special_keys & ALT_KEYS).any? @shift = @shift_lock || (special_keys & SHIFT_KEYS).any? @ctrl = (special_keys & CTRL_KEYS).any? + + @text_keys = $args.inputs.text end end end diff --git a/lib/multiline.rb b/lib/multiline.rb index 2e4763a..f8ab292 100644 --- a/lib/multiline.rb +++ b/lib/multiline.rb @@ -65,7 +65,6 @@ def draw_override(ffi) end def handle_keyboard - text_keys = $args.inputs.text # On a Mac: # Home is Cmd + ↑ / Fn + ← # End is Cmd + ↓ / Fn + → @@ -103,7 +102,7 @@ def handle_keyboard else @on_unhandled_key.call(@down_keys.first, self) end - elsif text_keys.empty? + elsif @text_keys.empty? if @down_keys.include?(:delete) delete_forward unless @readonly @ensure_cursor_visible = true @@ -164,7 +163,7 @@ def handle_keyboard @on_unhandled_key.call(@down_keys.first, self) end else - insert(text_keys.join('')) unless @readonly + insert(@text_keys.join('')) unless @readonly @ensure_cursor_visible = true end end diff --git a/lib/text.rb b/lib/text.rb index 6eeb792..b6676c6 100644 --- a/lib/text.rb +++ b/lib/text.rb @@ -29,8 +29,6 @@ def draw_override(ffi) end def handle_keyboard - text_keys = $args.inputs.text - if @meta || @ctrl # TODO: undo/redo if @down_keys.include?(:a) @@ -57,7 +55,7 @@ def handle_keyboard else @on_unhandled_key.call(@down_keys.first, self) end - elsif text_keys.empty? + elsif @text_keys.empty? if @down_keys.include?(:delete) delete_forward unless @readonly elsif @down_keys.include?(:backspace) @@ -88,7 +86,7 @@ def handle_keyboard @on_unhandled_key.call(@down_keys.first, self) end else - insert(text_keys.join('')) unless @readonly + insert(@text_keys.join('')) unless @readonly @ensure_cursor_visible = true end end diff --git a/metadata/game_metadata.txt b/metadata/game_metadata.txt index d233a00..f6e73e8 100644 --- a/metadata/game_metadata.txt +++ b/metadata/game_metadata.txt @@ -3,7 +3,7 @@ devid=fascinationworks devtitle=Fascination Works gameid=dr-input gametitle=Dragon Ruby Input -version=0.0.22 +version=0.0.23 icon=metadata/icon.png # === Flags available at all licensing tiers === From df7a0bb3857d5d29e8f96896d3fcb6e7e830b379 Mon Sep 17 00:00:00 2001 From: Marc Heiligers Date: Sat, 15 Feb 2025 17:55:25 -0700 Subject: [PATCH 2/3] Document undocumented methods; deprecate on_clicked and add on_click --- CHANGELOG.md | 7 +++++-- README.md | 12 +++++++++--- lib/base.rb | 2 +- lib/multiline.rb | 2 +- lib/text.rb | 2 +- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c101de5..c92ea5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # v0.0.23 - 16 February 2025 -* Made `@text_keys` instance variable instead of local -* Wrote up notes on special keyboard handling with filtering example +* Exposed `@text_keys` as an instance variable instead of a local. +* Wrote up notes on custom keyboard handling with a filtering example. +* Documented `#delete_forward` and `#delete_back` methods. +* Deprecated `on_clicked` and added `on_click` in it's place. +* Documented `focused` argument and `#focused?` aliases for `focussed` and `#focussed?` # v0.0.22 - 9 February 2025 diff --git a/README.md b/README.md index b73db61..82ab064 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,9 @@ The argument list below will list `prompt_color` but not the individual `prompt_ * `word_wrap` - if the control should wrap (Boolean), default false * `readonly` - initial input read only state (Boolean), default false * `focussed` - initial input focus (Boolean), default false -* `on_clicked` - on click callback, receives 2 parameters, the click and the `Input` control instance, default NOOP +* `focused` - Alias for `focussed` +* `on_clicked` - Deprecated - use `on_click` +* `on_click` - on click callback, receives 2 parameters, the click and the `Input` control instance, default `NOOP` * `on_unhandled_key` - on unhandle key pressed callback, receives 2 parameters, the key and the `Input` control instance, default NOOP. This callback receives keys like `[tab]` and `[enter]` * `max_length` - maximum allowed length (Integer), default `false` which disables length checks * `fill_from_bottom` - fill the text from the bottom, like for a log or game terminal, default `false` @@ -112,6 +114,9 @@ The argument list below will list `prompt_color` but not the individual `prompt_ * `#replace(text)` - Alias for `#insert(text)` * `#replace_at(text, start, end = start)` - Alias for `#insert_at(text, start, end = start)` * `#append(text)` - Appends text the end of the value, without changing the selection +* `#delete_forward` - Deletes selection or the character to the right of the cursor location if the selection is empty (typically in response to the `Delete` key) +* `#delete_back` - Deletes selection or the character to the left of the cursor location if the selection is empty (typically in response to the `Backspace` key) +* `#current_selection` - Returns the currently selected text * `#current_selection` - Returns the currently selected text * `#current_line` - Returns the currently selected line object * `#current_word` - Returns the word currently under the cursor @@ -146,11 +151,12 @@ The argument list below will list `prompt_color` but not the individual `prompt_ * `#focus` - Focusses the instance. Note the instance will only receive the focus after it's rendered. This prevents multiple instances from handling the keyboard and mouse events in the same tick. * `#blur` - Removes the focus from the instance. This happens immediately and the instance will not process keyboard and some mouse events after being blurred. * `#focussed?` - Returns true if the input is focussed, false otherwise +* `#focused?` - Alias for `#focussed?` * `#value_changed?` - Returns true if the input value changed in the last tick, false otherwise -## Special keyboard handling +## Custom keyboard handling -There may be cases where you want to do some special keyboard handling, like filtering out certain characters. In order to do this, create a subclass of `Input::Text` or `Input::Multiline` and override the `handle_keyboard` method, calling `super` when your special handling is done. The following instance variables are available: +There may be cases where you want to do some custom keyboard handling, like filtering out certain characters. In order to do this, create a subclass of `Input::Text` or `Input::Multiline` and override the `handle_keyboard` method, calling `super` when your special handling is done. The following instance variables are available: * `@meta` - true if the Meta key is down (the Command key on a Mac, or Windows key on Windows) * `@alt` - true if the Alt key is down diff --git a/lib/base.rb b/lib/base.rb index a12be85..173feed 100644 --- a/lib/base.rb +++ b/lib/base.rb @@ -73,7 +73,7 @@ def initialize(**params) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticCo @focussed = params[:focussed] || params[:focused] || false @will_focus = false # Get the focus at the end of the tick - @on_clicked = params[:on_clicked] || NOOP + @on_click = params[:on_click] || params[:on_clicked] || NOOP # @on_handled_key = params[:on_handled_key] || NOOP @on_unhandled_key = params[:on_unhandled_key] || NOOP diff --git a/lib/multiline.rb b/lib/multiline.rb index f8ab292..5b866c6 100644 --- a/lib/multiline.rb +++ b/lib/multiline.rb @@ -302,7 +302,7 @@ def handle_mouse # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity @selection_end = index @mouse_down = false if mouse.up else # clicking - @on_clicked.call(mouse, self) + @on_click.call(mouse, self) return unless (@focussed || @will_focus) && mouse.button_left if @shift diff --git a/lib/text.rb b/lib/text.rb index b6676c6..bb26498 100644 --- a/lib/text.rb +++ b/lib/text.rb @@ -108,7 +108,7 @@ def handle_mouse # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity @selection_end = index @mouse_down = false if mouse.up else - @on_clicked.call(mouse, self) + @on_click.call(mouse, self) return unless @focussed || @will_focus @mouse_down = true From 6972e9ad326034537344b7e49f74fd38f6bfee29 Mon Sep 17 00:00:00 2001 From: Marc Heiligers Date: Sun, 16 Feb 2025 16:15:11 -0700 Subject: [PATCH 3/3] Deprecate on_clicked and add on_click --- CHANGELOG.md | 2 +- README.md | 2 +- app/book_sample.rb | 4 ++-- lib/line_collection.rb | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c92ea5c..a0939a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# v0.0.23 - 16 February 2025 +# v0.0.23 - 17 February 2025 * Exposed `@text_keys` as an instance variable instead of a local. * Wrote up notes on custom keyboard handling with a filtering example. diff --git a/README.md b/README.md index 82ab064..ed63fea 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ The argument list below will list `prompt_color` but not the individual `prompt_ * `focused` - Alias for `focussed` * `on_clicked` - Deprecated - use `on_click` * `on_click` - on click callback, receives 2 parameters, the click and the `Input` control instance, default `NOOP` -* `on_unhandled_key` - on unhandle key pressed callback, receives 2 parameters, the key and the `Input` control instance, default NOOP. This callback receives keys like `[tab]` and `[enter]` +* `on_unhandled_key` - on unhandle key pressed callback, receives 2 parameters, the key and the `Input` control instance, default NOOP. This callback receives keys like `[tab]` and `[enter]` as symbols, so `:tab` and `:enter` * `max_length` - maximum allowed length (Integer), default `false` which disables length checks * `fill_from_bottom` - fill the text from the bottom, like for a log or game terminal, default `false` * `draw_autocomplete_menu` - if the input draws the autocomplete menu automatically, default `true` diff --git a/app/book_sample.rb b/app/book_sample.rb index 96c8528..bffc30f 100644 --- a/app/book_sample.rb +++ b/app/book_sample.rb @@ -44,7 +44,7 @@ def tick(args) args.state.multiline.focus end end, - on_clicked: lambda do |_mouse, input| + on_click: lambda do |_mouse, input| input.focus args.state.multiline.blur end, @@ -70,7 +70,7 @@ def tick(args) args.state.text.focus end end, - on_clicked: lambda do |_mouse, input| + on_click: lambda do |_mouse, input| input.focus args.state.text.blur end diff --git a/lib/line_collection.rb b/lib/line_collection.rb index 0b247cd..54a9fb8 100644 --- a/lib/line_collection.rb +++ b/lib/line_collection.rb @@ -233,7 +233,6 @@ def perform_word_wrap(text, width, first_line_number = 0, first_line_start = 0, # break up long words w = @font_style.string_width(word.rstrip) - # TODO: make this a binary search while w > width r = word.length - 1 l = 0