potty 0.0.1 → 0.0.2
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/CHANGELOG.md +41 -0
- data/README.md +16 -9
- data/bin/potty_inline_demo +78 -0
- data/lib/potty/ansi.rb +36 -0
- data/lib/potty/application.rb +6 -2
- data/lib/potty/input/decoder.rb +113 -0
- data/lib/potty/layout.rb +0 -23
- data/lib/potty/line_editor.rb +53 -0
- data/lib/potty/mouth.rb +217 -0
- data/lib/potty/surfaces/curses_surface.rb +13 -19
- data/lib/potty/surfaces/inline_surface.rb +62 -29
- data/lib/potty/theme.rb +14 -29
- data/lib/potty/version.rb +1 -1
- data/lib/potty/view.rb +15 -4
- data/lib/potty/widgets/button.rb +1 -1
- data/lib/potty/widgets/checkbox_group.rb +1 -1
- data/lib/potty/widgets/colored_fields_item.rb +1 -1
- data/lib/potty/widgets/countdown.rb +1 -1
- data/lib/potty/widgets/flash_message.rb +4 -4
- data/lib/potty/widgets/label.rb +1 -1
- data/lib/potty/widgets/list.rb +7 -7
- data/lib/potty/widgets/list_item.rb +22 -23
- data/lib/potty/widgets/panel.rb +1 -1
- data/lib/potty/widgets/radio_group.rb +9 -1
- data/lib/potty/widgets/status_bar.rb +1 -1
- data/lib/potty/widgets/text_input.rb +38 -54
- data/lib/potty/widgets/toggle.rb +1 -1
- data/lib/potty/window_manager.rb +6 -29
- data/lib/potty.rb +5 -0
- metadata +7 -1
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'curses'
|
|
4
3
|
require_relative 'base'
|
|
5
4
|
require_relative '../keys'
|
|
5
|
+
require_relative '../line_editor'
|
|
6
6
|
|
|
7
7
|
module Potty
|
|
8
8
|
module Widgets
|
|
9
9
|
# Single-line editable text field. Shows a block cursor when focused,
|
|
10
10
|
# a dim placeholder when empty and unfocused, and scrolls horizontally
|
|
11
|
-
# when the text outgrows the field.
|
|
11
|
+
# when the text outgrows the field. The editing model is a LineEditor
|
|
12
|
+
# (shared with the list InputItem); this widget owns rendering + scroll.
|
|
12
13
|
#
|
|
13
14
|
# Emits :change(text) on every edit. ASCII input only for now (matches
|
|
14
15
|
# the rest of the framework); UTF-8 entry would need multibyte getch.
|
|
15
16
|
class TextInput < Base
|
|
16
|
-
|
|
17
|
-
attr_accessor :placeholder, :max_length, :on_change
|
|
17
|
+
attr_accessor :placeholder, :on_change
|
|
18
18
|
|
|
19
19
|
def initialize(app, text: '', placeholder: '', max_length: nil, on_change: nil)
|
|
20
20
|
super(app)
|
|
21
|
-
@
|
|
21
|
+
@editor = LineEditor.new(text, max_length: max_length)
|
|
22
22
|
@placeholder = placeholder
|
|
23
|
-
@max_length = max_length
|
|
24
23
|
@on_change = on_change
|
|
25
|
-
@cursor = @text.length
|
|
26
24
|
@scroll = 0
|
|
27
25
|
end
|
|
28
26
|
|
|
@@ -30,32 +28,36 @@ module Potty
|
|
|
30
28
|
true
|
|
31
29
|
end
|
|
32
30
|
|
|
31
|
+
def text
|
|
32
|
+
@editor.text
|
|
33
|
+
end
|
|
34
|
+
|
|
33
35
|
def text=(value)
|
|
34
|
-
@text = value
|
|
35
|
-
@cursor = [@cursor, @text.length].min
|
|
36
|
+
@editor.text = value
|
|
36
37
|
notify_change
|
|
37
38
|
end
|
|
38
39
|
|
|
40
|
+
def max_length
|
|
41
|
+
@editor.max_length
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def max_length=(value)
|
|
45
|
+
@editor.max_length = value
|
|
46
|
+
end
|
|
47
|
+
|
|
39
48
|
def preferred_height(_width)
|
|
40
49
|
1
|
|
41
50
|
end
|
|
42
51
|
|
|
43
52
|
def handle_key(ch)
|
|
44
53
|
case ch
|
|
45
|
-
when Keys::LEFT
|
|
46
|
-
|
|
47
|
-
when Keys::
|
|
48
|
-
|
|
49
|
-
when Keys::
|
|
50
|
-
|
|
51
|
-
when Keys::
|
|
52
|
-
@cursor = @text.length
|
|
53
|
-
when Keys::DEL_ASCII, Keys::CTRL_H, Keys::BACKSPACE
|
|
54
|
-
backspace
|
|
55
|
-
when Keys::DELETE, Keys::CTRL_D
|
|
56
|
-
delete_forward
|
|
57
|
-
when Keys::SPACE..(Keys::DEL_ASCII - 1)
|
|
58
|
-
insert(ch.chr)
|
|
54
|
+
when Keys::LEFT then @editor.left
|
|
55
|
+
when Keys::RIGHT then @editor.right
|
|
56
|
+
when Keys::HOME, Keys::CTRL_A then @editor.home
|
|
57
|
+
when Keys::END_, Keys::CTRL_E then @editor.to_end
|
|
58
|
+
when Keys::DEL_ASCII, Keys::CTRL_H, Keys::BACKSPACE then changed(@editor.backspace)
|
|
59
|
+
when Keys::DELETE, Keys::CTRL_D then changed(@editor.delete_forward)
|
|
60
|
+
when Keys::SPACE..(Keys::DEL_ASCII - 1) then changed(@editor.insert(ch.chr))
|
|
59
61
|
else
|
|
60
62
|
return false
|
|
61
63
|
end
|
|
@@ -68,68 +70,50 @@ module Potty
|
|
|
68
70
|
width = @rect.width
|
|
69
71
|
adjust_scroll(width)
|
|
70
72
|
|
|
71
|
-
if
|
|
73
|
+
if text.empty? && !@focused
|
|
72
74
|
window.setpos(@rect.y, @rect.x)
|
|
73
|
-
window.attron(theme
|
|
75
|
+
window.attron(theme.style(:dim)) do
|
|
74
76
|
window.addstr(@placeholder.to_s[0, width].to_s.ljust(width))
|
|
75
77
|
end
|
|
76
78
|
return
|
|
77
79
|
end
|
|
78
80
|
|
|
79
|
-
visible = (
|
|
81
|
+
visible = (text[@scroll, width] || '').ljust(width)
|
|
80
82
|
window.setpos(@rect.y, @rect.x)
|
|
81
|
-
window.attron(theme
|
|
83
|
+
window.attron(theme.style(:normal)) { window.addstr(visible) }
|
|
82
84
|
|
|
83
85
|
return unless @focused
|
|
84
86
|
|
|
85
87
|
# Block cursor: reverse-video the cell under the caret.
|
|
86
|
-
col = @cursor - @scroll
|
|
88
|
+
col = @editor.cursor - @scroll
|
|
87
89
|
return if col.negative? || col >= width
|
|
88
90
|
|
|
89
|
-
char_under =
|
|
91
|
+
char_under = text[@editor.cursor] || ' '
|
|
90
92
|
window.setpos(@rect.y, @rect.x + col)
|
|
91
|
-
window.attron(theme
|
|
93
|
+
window.attron(theme.style(:normal, reverse: true)) do
|
|
92
94
|
window.addstr(char_under)
|
|
93
95
|
end
|
|
94
96
|
end
|
|
95
97
|
|
|
96
98
|
private
|
|
97
99
|
|
|
98
|
-
def
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@text.insert(@cursor, str)
|
|
102
|
-
@cursor += str.length
|
|
103
|
-
notify_change
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def backspace
|
|
107
|
-
return if @cursor.zero?
|
|
108
|
-
|
|
109
|
-
@text.slice!(@cursor - 1)
|
|
110
|
-
@cursor -= 1
|
|
111
|
-
notify_change
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def delete_forward
|
|
115
|
-
return if @cursor >= @text.length
|
|
116
|
-
|
|
117
|
-
@text.slice!(@cursor)
|
|
118
|
-
notify_change
|
|
100
|
+
def changed(did_change)
|
|
101
|
+
notify_change if did_change
|
|
119
102
|
end
|
|
120
103
|
|
|
121
104
|
def adjust_scroll(width)
|
|
122
105
|
return if width <= 0
|
|
123
106
|
|
|
124
|
-
|
|
125
|
-
@scroll =
|
|
107
|
+
cursor = @editor.cursor
|
|
108
|
+
@scroll = cursor - width + 1 if cursor - @scroll >= width
|
|
109
|
+
@scroll = cursor if cursor < @scroll
|
|
126
110
|
@scroll = [@scroll, 0].max
|
|
127
111
|
end
|
|
128
112
|
|
|
129
113
|
def notify_change
|
|
130
114
|
# Hand listeners a snapshot, not the live internal buffer, so a
|
|
131
115
|
# consumer that stores the value doesn't see it mutate underfoot.
|
|
132
|
-
snapshot =
|
|
116
|
+
snapshot = text.dup
|
|
133
117
|
@on_change&.call(snapshot)
|
|
134
118
|
emit(:change, snapshot)
|
|
135
119
|
end
|
data/lib/potty/widgets/toggle.rb
CHANGED
|
@@ -55,7 +55,7 @@ module Potty
|
|
|
55
55
|
|
|
56
56
|
knob = @value ? "[\u25CF]" : "[\u25CB]"
|
|
57
57
|
text = "#{knob} #{@label}"[0, @rect.width]
|
|
58
|
-
attr = @focused ? theme.
|
|
58
|
+
attr = @focused ? theme.style(:selected, bold: true) : theme.style(:normal)
|
|
59
59
|
|
|
60
60
|
window.setpos(@rect.y, @rect.x)
|
|
61
61
|
window.attron(attr) { window.addstr(text) }
|
data/lib/potty/window_manager.rb
CHANGED
|
@@ -3,53 +3,30 @@
|
|
|
3
3
|
require 'curses'
|
|
4
4
|
|
|
5
5
|
module Potty
|
|
6
|
-
#
|
|
6
|
+
# Holds the curses stdscr and screen dimensions, and batches the
|
|
7
|
+
# refresh (noutrefresh + doupdate). Backs CursesSurface.
|
|
7
8
|
class WindowManager
|
|
8
9
|
attr_reader :stdscr, :max_y, :max_x
|
|
9
10
|
|
|
10
11
|
def initialize
|
|
11
|
-
@windows = {}
|
|
12
12
|
@stdscr = nil
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
# Called during application setup
|
|
15
|
+
# Called during application setup.
|
|
16
16
|
def setup(stdscr)
|
|
17
17
|
@stdscr = stdscr
|
|
18
18
|
update_dimensions
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def update_dimensions
|
|
22
|
-
@max_y
|
|
22
|
+
@max_y = @stdscr.maxy
|
|
23
|
+
@max_x = @stdscr.maxx
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
#
|
|
26
|
-
def create_window(name, height, width, y, x)
|
|
27
|
-
win = ::Curses::Window.new(height, width, y, x)
|
|
28
|
-
@windows[name] = win
|
|
29
|
-
win
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Get existing window
|
|
33
|
-
def get_window(name)
|
|
34
|
-
@windows[name]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Destroy window
|
|
38
|
-
def destroy_window(name)
|
|
39
|
-
@windows[name]&.close
|
|
40
|
-
@windows.delete(name)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Refresh all windows efficiently
|
|
26
|
+
# Flush buffered drawing to the screen.
|
|
44
27
|
def refresh_all
|
|
45
28
|
@stdscr.noutrefresh
|
|
46
|
-
@windows.values.each(&:noutrefresh)
|
|
47
29
|
::Curses.doupdate
|
|
48
30
|
end
|
|
49
|
-
|
|
50
|
-
def clear_all
|
|
51
|
-
@stdscr.clear
|
|
52
|
-
@windows.values.each(&:clear)
|
|
53
|
-
end
|
|
54
31
|
end
|
|
55
32
|
end
|
data/lib/potty.rb
CHANGED
|
@@ -4,6 +4,9 @@ require_relative 'potty/version'
|
|
|
4
4
|
require_relative 'potty/keys'
|
|
5
5
|
require_relative 'potty/events'
|
|
6
6
|
require_relative 'potty/style'
|
|
7
|
+
require_relative 'potty/line_editor'
|
|
8
|
+
require_relative 'potty/ansi'
|
|
9
|
+
require_relative 'potty/input/decoder'
|
|
7
10
|
require_relative 'potty/application'
|
|
8
11
|
require_relative 'potty/theme'
|
|
9
12
|
require_relative 'potty/layout'
|
|
@@ -30,6 +33,8 @@ require_relative 'potty/widgets/spinner'
|
|
|
30
33
|
require_relative 'potty/sprite'
|
|
31
34
|
require_relative 'potty/animator'
|
|
32
35
|
require_relative 'potty/sprites/sample'
|
|
36
|
+
# Mouth's prompt views subclass Potty::View, so load it after the framework.
|
|
37
|
+
require_relative 'potty/mouth'
|
|
33
38
|
|
|
34
39
|
module Potty
|
|
35
40
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: potty
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- TwilightCoders
|
|
@@ -43,6 +43,7 @@ email:
|
|
|
43
43
|
- info@twilightcoders.net
|
|
44
44
|
executables:
|
|
45
45
|
- potty_demo
|
|
46
|
+
- potty_inline_demo
|
|
46
47
|
extensions: []
|
|
47
48
|
extra_rdoc_files: []
|
|
48
49
|
files:
|
|
@@ -50,14 +51,19 @@ files:
|
|
|
50
51
|
- LICENSE.txt
|
|
51
52
|
- README.md
|
|
52
53
|
- bin/potty_demo
|
|
54
|
+
- bin/potty_inline_demo
|
|
53
55
|
- examples/test_view.rb
|
|
54
56
|
- lib/potty.rb
|
|
55
57
|
- lib/potty/animator.rb
|
|
58
|
+
- lib/potty/ansi.rb
|
|
56
59
|
- lib/potty/application.rb
|
|
57
60
|
- lib/potty/border.rb
|
|
58
61
|
- lib/potty/events.rb
|
|
62
|
+
- lib/potty/input/decoder.rb
|
|
59
63
|
- lib/potty/keys.rb
|
|
60
64
|
- lib/potty/layout.rb
|
|
65
|
+
- lib/potty/line_editor.rb
|
|
66
|
+
- lib/potty/mouth.rb
|
|
61
67
|
- lib/potty/sprite.rb
|
|
62
68
|
- lib/potty/sprites/sample.rb
|
|
63
69
|
- lib/potty/style.rb
|