tuile 0.15.0 → 0.16.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +121 -80
  3. data/README.md +28 -12
  4. data/book/04-event-loop.md +12 -12
  5. data/book/05-focus.md +95 -26
  6. data/book/06-theming.md +58 -26
  7. data/book/07-components.md +61 -6
  8. data/book/08-testing.md +24 -22
  9. data/book/10-locale.md +2 -2
  10. data/book/README.md +6 -5
  11. data/examples/file_commander.rb +14 -5
  12. data/examples/hello_world.rb +17 -4
  13. data/examples/sampler.rb +392 -18
  14. data/lib/tuile/component/abstract_string_field.rb +16 -18
  15. data/lib/tuile/component/abstract_wrapping_field.rb +47 -11
  16. data/lib/tuile/component/button.rb +8 -8
  17. data/lib/tuile/component/checkbox.rb +9 -9
  18. data/lib/tuile/component/checkbox_group.rb +6 -5
  19. data/lib/tuile/component/combo_box.rb +50 -35
  20. data/lib/tuile/component/confirm_window.rb +7 -5
  21. data/lib/tuile/component/date_field.rb +28 -3
  22. data/lib/tuile/component/date_time_field.rb +275 -0
  23. data/lib/tuile/component/has_bad_input.rb +2 -2
  24. data/lib/tuile/component/has_content.rb +3 -3
  25. data/lib/tuile/component/has_placeholder.rb +1 -1
  26. data/lib/tuile/component/has_validation.rb +2 -2
  27. data/lib/tuile/component/has_value.rb +1 -1
  28. data/lib/tuile/component/label.rb +1 -1
  29. data/lib/tuile/component/layout/box.rb +4 -1
  30. data/lib/tuile/component/layout.rb +3 -3
  31. data/lib/tuile/component/list.rb +42 -32
  32. data/lib/tuile/component/list_dropdown.rb +3 -3
  33. data/lib/tuile/component/menu_bar/cascade.rb +5 -5
  34. data/lib/tuile/component/menu_bar.rb +18 -18
  35. data/lib/tuile/component/notification.rb +32 -18
  36. data/lib/tuile/component/overlay.rb +9 -8
  37. data/lib/tuile/component/picker_window.rb +27 -8
  38. data/lib/tuile/component/popup.rb +2 -2
  39. data/lib/tuile/component/progress_bar.rb +10 -4
  40. data/lib/tuile/component/radio_group.rb +6 -5
  41. data/lib/tuile/component/select.rb +11 -12
  42. data/lib/tuile/component/slot.rb +3 -3
  43. data/lib/tuile/component/tab_sheet.rb +6 -6
  44. data/lib/tuile/component/tabs.rb +11 -11
  45. data/lib/tuile/component/text_area.rb +12 -10
  46. data/lib/tuile/component/text_field.rb +14 -12
  47. data/lib/tuile/component/text_view.rb +15 -11
  48. data/lib/tuile/component/time_field.rb +29 -4
  49. data/lib/tuile/component.rb +201 -93
  50. data/lib/tuile/event_queue.rb +4 -4
  51. data/lib/tuile/fake_event_queue.rb +1 -1
  52. data/lib/tuile/fake_screen.rb +84 -2
  53. data/lib/tuile/mouse/router.rb +217 -0
  54. data/lib/tuile/mouse.rb +177 -0
  55. data/lib/tuile/screen.rb +98 -61
  56. data/lib/tuile/screen_pane.rb +41 -36
  57. data/lib/tuile/styled_string.rb +5 -5
  58. data/lib/tuile/testing.rb +8 -8
  59. data/lib/tuile/theme.rb +22 -34
  60. data/lib/tuile/version.rb +1 -1
  61. data/lib/tuile/vertical_scroll_bar.rb +1 -1
  62. data/sig/tuile.rbs +1211 -427
  63. metadata +4 -16
  64. data/COMPARISON.md +0 -101
  65. data/DECISIONS.md +0 -8562
  66. data/TERMINOLOGY.md +0 -85
  67. data/ideas/arrow-key-navigation.md +0 -221
  68. data/ideas/binder.md +0 -177
  69. data/ideas/composite-field.md +0 -77
  70. data/ideas/focus-accent.md +0 -116
  71. data/ideas/form-layout.md +0 -151
  72. data/ideas/hover/probe.rb +0 -241
  73. data/ideas/hover/probe_spec.rb +0 -82
  74. data/ideas/hover.md +0 -909
  75. data/ideas/modal-backdrop.md +0 -24
  76. data/ideas/new-components.md +0 -144
  77. data/ideas/per-component-buffers.md +0 -55
  78. data/lib/tuile/mouse_event.rb +0 -68
data/book/10-locale.md CHANGED
@@ -181,7 +181,7 @@ forever.
181
181
 
182
182
  ## When the locale changes under you
183
183
 
184
- {Tuile::Screen#locale=} fires {Tuile::Component#on_locale_changed}
184
+ {Tuile::Screen#locale=} fires {Tuile::Component#handle_locale_changed}
185
185
  across the attached tree and then invalidates all of it — the same
186
186
  machinery {Tuile::Screen#theme=} uses, for the same reason.
187
187
 
@@ -194,7 +194,7 @@ The hook is for state you *pushed* somewhere when you last read the
194
194
  conventions. A date field's typing hint is the worked example: it lives
195
195
  in its editor's `placeholder`, written when the formats were last set, so
196
196
  a repaint alone would faithfully repaint the stale `dd.mm.yyyy`. The
197
- field overrides `on_locale_changed` to re-derive it — and, while it is
197
+ field overrides `handle_locale_changed` to re-derive it — and, while it is
198
198
  there, to rewrite a buffer that still parses into the new primary format.
199
199
  Your own code does the same for a date you rendered into a
200
200
  {Tuile::Component::Label}, either by overriding the hook or by assigning
data/book/README.md CHANGED
@@ -65,16 +65,17 @@ one, not to fill an outline.
65
65
  same queue rather than handled off the signal.
66
66
  5. **[Focus and the keyboard](05-focus.md).** The focus chain and
67
67
  `focusable?`, and the three-rung order in which a keystroke is offered
68
- to the tree — Tab, global shortcuts, then `handle_key` delivered to
68
+ to the tree — Tab, global shortcuts, then `handle_key?` delivered to
69
69
  focus and bubbling up its ancestors. Why scope-wide keys (pane jumps, a
70
70
  form's default button) belong on an ancestor, why a paste rides its own
71
- path rather than the ladder, and how to write a status line over
72
- `on_focus_changed` — Tuile draws none for you.
71
+ path rather than the ladder, and how the mouse takes a road of its own —
72
+ a press bubbling to one claimant that is then grabbed. Ends on writing a
73
+ status line over `on_focus_changed` — Tuile draws none for you.
73
74
  6. **[Theming](06-theming.md).** Semantic color tokens read at paint
74
75
  time, opt-in component backgrounds that inherit down the tree
75
76
  (`bg_color`), light/dark auto-detection at startup and live OS
76
77
  appearance flips, pairing variants in a `ThemeDef`, app-specific custom
77
- tokens, and rebuilding theme-derived content in `on_theme_changed`.
78
+ tokens, and rebuilding theme-derived content in `handle_theme_changed`.
78
79
  7. **[The component library](07-components.md).** A narrative tour of
79
80
  the shipped toolbox — the text inputs and views, the value fields, the
80
81
  selectors, Button, ProgressBar, Window, TabSheet, MenuBar, Popup and the
@@ -94,5 +95,5 @@ one, not to fill an outline.
94
95
  never prose*, detected from `locale(1)` only when the environment
95
96
  actually asked, with `Locale::ISO` as the floor. Why the name tables
96
97
  are keyed by the `Date` accessor that reads them, how a field follows
97
- the session until you override it, and what `on_locale_changed` is
98
+ the session until you override it, and what `handle_locale_changed` is
98
99
  for.
@@ -20,6 +20,14 @@ require "rainbow"
20
20
  require "tuile"
21
21
 
22
22
  module FileCommanderExample
23
+ # `hint` is the app's token, not Tuile's — the framework carries accents only
24
+ # for the chrome it paints itself, and the status line below is ours. Paired
25
+ # in a ThemeDef so it survives an OS appearance flip.
26
+ APP_THEME = Tuile::ThemeDef.new(
27
+ dark: Tuile::Theme::DARK.with(custom: { hint: Tuile::Color::GREY54 }),
28
+ light: Tuile::Theme::LIGHT.with(custom: { hint: Tuile::Color::GREY62 })
29
+ )
30
+
23
31
  # Pastel X11 colors chosen to read on a black background.
24
32
  TYPE_COLORS = {
25
33
  directory: :lightskyblue,
@@ -45,7 +53,7 @@ module FileCommanderExample
45
53
  attr_reader :cwd
46
54
  attr_accessor :on_cwd_changed
47
55
 
48
- def handle_key(key)
56
+ def handle_key?(key)
49
57
  return false unless active?
50
58
 
51
59
  if Tuile::Keys::BACKSPACES.include?(key)
@@ -56,7 +64,7 @@ module FileCommanderExample
56
64
  end
57
65
  end
58
66
 
59
- def on_focus
67
+ def handle_focus
60
68
  super
61
69
  @on_cwd_changed&.call
62
70
  end
@@ -135,13 +143,13 @@ module FileCommanderExample
135
143
  # The status line. Every key here works in both panes, so the row never
136
144
  # changes and nothing needs to watch focus — a status line is only worth
137
145
  # wiring to Tuile::Screen#on_focus_changed= when its text actually varies
138
- # with the focused component. `theme.hint` bakes its colors in, so the
146
+ # with the focused component. `theme.fg` bakes its colors in, so the
139
147
  # one thing this label does watch is a light/dark flip.
140
148
  @status = Tuile::Component::Label.new
141
149
  render_status = lambda do
142
150
  t = screen.theme
143
- @status.text = "q #{t.hint("quit")} Tab #{t.hint("Switch")} " \
144
- "Enter #{t.hint("Open")} Bksp #{t.hint("Up")}"
151
+ @status.text = "q #{t.fg(:hint, "quit")} Tab #{t.fg(:hint, "Switch")} " \
152
+ "Enter #{t.fg(:hint, "Open")} Bksp #{t.fg(:hint, "Up")}"
145
153
  end
146
154
  render_status.call
147
155
  @status.on_theme_changed = render_status
@@ -180,6 +188,7 @@ unless File.directory?(start_dir)
180
188
  end
181
189
 
182
190
  screen = Tuile::Screen.new
191
+ screen.theme_def = FileCommanderExample::APP_THEME
183
192
  commander = FileCommanderExample::FileCommander.new(start_dir, start_dir)
184
193
  screen.content = commander
185
194
  commander.left_window.focus
@@ -11,18 +11,31 @@
11
11
 
12
12
  require "tuile"
13
13
 
14
+ # `hint` is the app's token, not Tuile's: the framework carries accents for the
15
+ # chrome *it* paints, and a status line is the app's own (Tuile draws none).
16
+ # Pairing the two shades in a ThemeDef is what makes it survive the user
17
+ # flipping OS appearance — a bare `theme=` would be replaced on the next flip.
18
+ # Both greys quantize to :bright_black on a 16-color terminal, so the
19
+ # description stays dimmer than the key beside it even there.
20
+ APP_THEME = Tuile::ThemeDef.new(
21
+ dark: Tuile::Theme::DARK.with(custom: { hint: Tuile::Color::GREY54 }),
22
+ light: Tuile::Theme::LIGHT.with(custom: { hint: Tuile::Color::GREY62 })
23
+ )
24
+
14
25
  # Screen must exist before any Component is built: components reach for
15
26
  # Tuile::Screen.instance during invalidate/repaint hooks.
16
27
  screen = Tuile::Screen.new
28
+ screen.theme_def = APP_THEME
17
29
 
18
30
  window = Tuile::Component::Window.new("Tuile")
19
31
  window.content = Tuile::Component::Label.new("Hello, world!")
20
32
 
21
- # The status line. `theme.hint` styles the *description* half of a "key what"
22
- # pair, and bakes the color in — so the label rebuilds itself from
23
- # `on_theme_changed` to follow a light/dark flip.
33
+ # The status line. `theme.fg` styles the *description* half of a "key what"
34
+ # pair — dimmed, so the key is the element that pulls the eye — and bakes the
35
+ # color in, so the label rebuilds itself from its `on_theme_changed` slot to follow a
36
+ # light/dark flip.
24
37
  status = Tuile::Component::Label.new
25
- render_status = -> { status.text = "q #{screen.theme.hint("quit")}" }
38
+ render_status = -> { status.text = "q #{screen.theme.fg(:hint, "quit")}" }
26
39
  render_status.call
27
40
  status.on_theme_changed = render_status
28
41