window_blessing 0.0.1 → 0.0.4

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 (39) hide show
  1. data/CHANGELOG.md +30 -0
  2. data/README.md +1 -1
  3. data/bin/buffered_screen_demo.rb +4 -4
  4. data/bin/color_picker_demo.rb +1 -0
  5. data/bin/windowed_screen_demo.rb +1 -1
  6. data/bin/xterm_screen_demo.rb +6 -4
  7. data/lib/window_blessing.rb +2 -2
  8. data/lib/window_blessing/buffer.rb +16 -6
  9. data/lib/window_blessing/buffered_screen.rb +2 -2
  10. data/lib/window_blessing/color.rb +1 -0
  11. data/lib/window_blessing/debug_tools/log_request_redraw_internal.rb +16 -0
  12. data/lib/window_blessing/event_manager.rb +11 -6
  13. data/lib/window_blessing/evented.rb +1 -1
  14. data/lib/window_blessing/evented_variable.rb +10 -1
  15. data/lib/window_blessing/tools.rb +11 -1
  16. data/lib/window_blessing/version.rb +1 -1
  17. data/lib/window_blessing/widgets/draggable_background.rb +1 -1
  18. data/lib/window_blessing/widgets/label.rb +1 -1
  19. data/lib/window_blessing/widgets/slider.rb +10 -2
  20. data/lib/window_blessing/widgets/text_field.rb +1 -1
  21. data/lib/window_blessing/window.rb +48 -45
  22. data/lib/window_blessing/window_redraw_areas.rb +23 -0
  23. data/lib/window_blessing/windowed_screen.rb +11 -13
  24. data/lib/window_blessing/xterm_event_parser.rb +68 -59
  25. data/lib/window_blessing/xterm_screen.rb +2 -2
  26. data/lib/window_blessing/xterm_state.rb +1 -1
  27. data/spec/buffer_spec.rb +56 -0
  28. data/spec/color_spec.rb +14 -0
  29. data/spec/event_manager_spec.rb +107 -0
  30. data/spec/event_queue_spec.rb +46 -0
  31. data/spec/evented_variable_spec.rb +43 -0
  32. data/spec/tools_spec.rb +18 -0
  33. data/spec/window_redraw_areas_spec.rb +79 -0
  34. data/spec/window_spec.rb +138 -3
  35. data/spec/xterm_event_parser_spec.rb +86 -0
  36. data/window_blessing.gemspec +4 -3
  37. metadata +20 -11
  38. data/bin/foiled_demo.rb +0 -27
  39. data/bin/text_editor_demo.rb +0 -292
@@ -0,0 +1,23 @@
1
+ module WindowBlessing
2
+ class WindowRedrawAreas
3
+ include Tools
4
+ attr_reader :areas
5
+
6
+ def initialize
7
+ @areas = []
8
+ end
9
+
10
+ # first pass merging algorithm:
11
+ # merge all overlapping areas with area until no overlapping areas
12
+ # This creates a non-overlapping set of areas, but it may be much bigger total area than before.
13
+ # TODO - for "two-point" overlaps - just shrink one of the rectangles so they don't overlap but still cover the same area
14
+ def <<(area)
15
+ overlapping, non_overlapping = @areas.partition {|a| a.overlaps?(area)}
16
+ while overlapping.length > 0
17
+ overlapping.each {|a| area &= a}
18
+ overlapping, non_overlapping = non_overlapping.partition {|a| a.overlaps?(area)}
19
+ end
20
+ @areas = non_overlapping + [area]
21
+ end
22
+ end
23
+ end
@@ -20,32 +20,30 @@ class WindowedScreen < XtermScreen
20
20
  @root_window.buffer.dirty
21
21
  @root_window.name = "root_window"
22
22
 
23
- event_manager.add_handler :tick do
24
- if redraw_area = root_window.requested_redraw_area
25
- time(:redraw, "size=#{redraw_area.size}.area = #{redraw_area.size.x * redraw_area.size.y}") do
26
- root_window.draw
27
- buffer = root_window.buffer
28
- output.draw_buffer buffer.dirty_area.loc, buffer.dirty_subbuffer if buffer.dirty_area
29
- buffer.clean
30
- end
23
+ event_manager.on :tick do
24
+ if root_window.redraw_requested?
25
+ root_window.draw
26
+ buffer = root_window.buffer
27
+ output.draw_buffer buffer.dirty_area.loc, buffer.dirty_subbuffer if buffer.dirty_area
28
+ buffer.clean
31
29
  end
32
30
  end
33
31
 
34
- event_manager.add_handler :key_press do |event|
32
+ event_manager.on :key_press do |event|
35
33
  root_window.route_keyboard_event event
36
34
  end
37
35
 
38
- event_manager.add_handler :string_input do |event|
36
+ event_manager.on :string_input do |event|
39
37
  root_window.route_keyboard_event event
40
38
  end
41
39
 
42
- event_manager.add_handler :resize do |event|
40
+ event_manager.on :resize do |event|
43
41
  root_window.size = event[:size]
44
42
  root_window.request_redraw_internal
45
43
  end
46
44
 
47
- event_manager.add_handler :pointer do |event|
48
- root_window.pointer_event event.clone
45
+ event_manager.on :pointer do |event|
46
+ root_window.route_pointer_event event.clone
49
47
  end
50
48
  end
51
49
 
@@ -13,7 +13,7 @@ class XtermEventParser < BabelBridge::Parser
13
13
  end
14
14
 
15
15
  rule :event, /[^\x00-\x1f\x7F]+/ do
16
- def event; {:type => :string_input, :string => to_s} end
16
+ def event; {type: :string_input, string: to_s} end
17
17
  end
18
18
 
19
19
  rule :command, /[a-zA-Z]/
@@ -27,28 +27,27 @@ class XtermEventParser < BabelBridge::Parser
27
27
  rule :event, "\e[8;", :numbers, "t" do
28
28
  include GuiGeo
29
29
  def event
30
- {:type => :xterm_state, :state_type => :size, :state => point(*numbers.to_a.reverse)}
30
+ {type: :xterm_state, state_type: :size, state: point(*numbers.to_a.reverse)}
31
31
  end
32
32
  end
33
33
 
34
34
  rule :event, "\e[4;", :numbers, "t" do
35
35
  include GuiGeo
36
36
  def event
37
- {:type => :xterm_state, :state_type => :display_pixel_size, :state => point(*numbers.to_a.reverse)}
37
+ {type: :xterm_state, state_type: :display_pixel_size, state: point(*numbers.to_a.reverse)}
38
38
  end
39
39
  end
40
40
 
41
- rule(:event, "\e\x7F") {def event;{:type => [:key_press,:backspace], :key => :backspace, :modifiers => [:alt]};end}
42
- rule(:event, "\x7F") {def event;{:type => [:key_press,:backspace], :key => :backspace, :modifiers => []};end}
43
- rule(:event, "\e[O") {def event;{:type => :blur};end}
44
- rule(:event, "\e[I") {def event;{:type => :focus};end}
41
+ rule(:event, "\e\x7F") {def event;{type: [:key_press,:backspace], key: :backspace, alt:true };end}
42
+ rule(:event, "\x7F") {def event;{type: [:key_press,:backspace], key: :backspace};end}
43
+ rule(:event, "\e[O") {def event;{type: :blur};end}
44
+ rule(:event, "\e[I") {def event;{type: :focus};end}
45
45
 
46
46
  rule :event, :key_press do
47
- def event; {:type => [:key_press,key], :key => key, :modifiers => modifiers} end
47
+ def event; {type: [:key_press,key], key: key}.merge(modifiers) end
48
48
 
49
49
  def modifiers
50
- m = key_press.modifier
51
- m ? m.modifiers : []
50
+ key_press.modifier ? key_press.modifier.modifiers : {}
52
51
  end
53
52
  end
54
53
 
@@ -58,83 +57,89 @@ class XtermEventParser < BabelBridge::Parser
58
57
  rule(:key_press, "\e", :modifier, "C") {def key;:right;end}
59
58
  rule(:key_press, "\e", :modifier, "A") {def key;:up;end}
60
59
  rule(:key_press, "\e", :modifier, "Z") {def key;:reverse_tab;end}
61
- rule(:key_press, "\e", :modifier, "H") {def key;:home;end}
62
- rule(:key_press, "\e", :modifier, "F") {def key;:end;end}
63
60
  rule(:key_press, "\e", :modifier, "P") {def key;:f1;end}
64
61
  rule(:key_press, "\e", :modifier, "Q") {def key;:f2;end}
65
62
  rule(:key_press, "\e", :modifier, "R") {def key;:f3;end}
66
63
  rule(:key_press, "\e", :modifier, "S") {def key;:f4;end}
67
- rule(:key_press, "\e", :modifier, "F") {def key;:home;end}
68
- rule(:key_press, "\e", :modifier, "H") {def key;:end;end}
64
+ rule(:key_press, "\e", :modifier, "F") {def key;:end;end;}
65
+ rule(:key_press, "\e", :modifier, "H") {def key;:home;end; }
69
66
 
70
67
  rule :modifier, "\e", :modifier do
71
68
  def modifiers
72
- (modifier.modifiers || []) + [:alt]
69
+ (modifier.modifiers || {}).merge alt:true
73
70
  end
74
-
75
71
  end
72
+
73
+ MODIFER_DECODER = {
74
+ 2 => {shift: true},
75
+ 3 => {alt: true},
76
+ 4 => {shift: true, alt: true},
77
+ 5 => {control: true},
78
+ 6 => {shift: true, control: true},
79
+ 7 => {alt: true, control: true},
80
+ 8 => {shift: true, alt: true, control: true},
81
+ 9 => {alt: true},
82
+ 10 => {shift: true, alt: true},
83
+ }
84
+
76
85
  rule :modifier, "[", :numbers do
77
86
  def modifiers
78
- {
79
- 2 => [:shift],
80
- 3 => [:alt],
81
- 4 => [:shift, :alt],
82
- 5 => [:control],
83
- 6 => [:shift, :control],
84
- 7 => [:alt, :control],
85
- 8 => [:shift, :alt, :control],
86
- }[numbers.to_a[-1].to_i] || []
87
+ MODIFER_DECODER[numbers.to_a[-1].to_i] || {}
87
88
  end
88
89
  end
89
90
 
90
91
  rule :modifier, /[\[O]/ do
91
- def modifiers; []; end
92
+ def modifiers; {}; end
92
93
  end
93
94
 
94
- rule :event, "\e[", :number, "~" do
95
+ KEY_MAP_DECODER = {
96
+ 3 => :delete,
97
+ 2 => :insert,
98
+ 6 => :page_down,
99
+ 5 => :page_up,
100
+ 13 => :f3,
101
+ 14 => :f4,
102
+ 15 => :f5,
103
+ 17 => :f6,
104
+ 18 => :f7,
105
+ 19 => :f8,
106
+ 20 => :f9,
107
+ 21 => :f10,
108
+ 23 => :f11,
109
+ 24 => :f12,
110
+ }
111
+ rule :event, "\e[", :number, match?(";2").as(:shift), "~" do
95
112
  def event
113
+ key = KEY_MAP_DECODER[number.to_i]
96
114
  {
97
- :type => :key_press,
98
- :key => {
99
- 3 => :delete,
100
- 2 => :insert,
101
- 6 => :page_down,
102
- 5 => :page_up,
103
- 13 => :f3,
104
- 14 => :f4,
105
- 15 => :f5,
106
- 17 => :f6,
107
- 18 => :f7,
108
- 19 => :f8,
109
- 20 => :f9,
110
- 21 => :f10,
111
- 23 => :f11,
112
- 24 => :f12,
113
- }[number.to_i]
115
+ :type => [:key_press, key],
116
+ :key => key,
117
+ :shift => !!shift
114
118
  }
115
119
  end
116
120
  end
117
121
 
122
+ BUTTON_ACTIONS = {
123
+ 32 => [:button_down, 1], 33 => [:button_down, 2], 34=> [:button_down, 3], 35=>:button_up,
124
+ 64 => :drag,
125
+ 96 => :wheel_down, 97 => :wheel_up
126
+ }
127
+
118
128
  rule :event, "\e[", "M", match(/.../).as(:state) do
129
+ include GuiGeo
119
130
  def event
120
131
  s, x, y = state.to_s.unpack "CCC"
121
132
  x -= 33
122
133
  y -= 33
123
- button_actions = {
124
- 32 => :button1_down, 33 => :button2_down, 34=> :button3_down, 35=>:button_up,
125
- 64 => :drag,
126
- 96 => :wheel_down, 97 => :wheel_up
127
- }
128
134
  {
129
- type: [:pointer, button_actions[s&99]],
130
- button: button_actions[s&99],
131
- state: s,
132
- loc: point(x,y)
133
- }.tap do |h|
134
- h[:shift_down] = true if (s&4)!=0
135
- h[:alt_down] = true if (s&8)!=0
136
- h[:control_down] = true if (s&16)!=0
137
- end
135
+ type: [:pointer, BUTTON_ACTIONS[s&99]].flatten,
136
+ button: BUTTON_ACTIONS[s&99],
137
+ state: s,
138
+ loc: point(x,y),
139
+ shift: (s&4)!=0,
140
+ alt: (s&8)!=0,
141
+ control: (s&16)!=0,
142
+ }
138
143
  end
139
144
  end
140
145
 
@@ -145,10 +150,14 @@ class XtermEventParser < BabelBridge::Parser
145
150
  end
146
151
  end
147
152
 
153
+ rule(:event, "\e") {def event; {type: [:key_press, :escape], :key=>:escape} end}
154
+ rule(:event, "\r") {def event; {type: [:key_press, :enter], :key=>:enter} end}
155
+ rule(:event, "\t") {def event; {type: [:key_press, :tab], :key=>:tab} end}
156
+
148
157
  rule :event, /[\x00-\x1f]/ do
149
158
  def event
150
159
  char = "%c"%(to_s.getbyte(0)+"`".getbyte(0))
151
- {:type => :key_press, :key => "control_#{char}".to_sym}
160
+ {type: [:key_press, char.to_sym], key: char.to_sym, control: true}
152
161
  end
153
162
  end
154
163
 
@@ -14,8 +14,8 @@ class XtermScreen
14
14
  @pending_events = []
15
15
  @event_queue = EventQueue.new
16
16
 
17
- @event_manager.add_handler :key_press do |event|
18
- quit if event[:key]==:control_q
17
+ @event_manager.on :key_press do |event|
18
+ quit if event[:control] && event[:key]==:q
19
19
  end
20
20
  end
21
21
 
@@ -5,7 +5,7 @@ class XtermState
5
5
  def initialize(event_manager)
6
6
  @state = {:size => point(-1,-1)}
7
7
 
8
- event_manager.add_handler :xterm_state do |event|
8
+ event_manager.on :xterm_state do |event|
9
9
  state_type = event[:state_type]
10
10
  old_state = state[state_type]
11
11
  new_state = event[:state]
@@ -47,6 +47,12 @@ describe "Buffer" do
47
47
  fb.to_s.should == "----\n----\n----\n----"
48
48
  end
49
49
 
50
+ it "fill out of bounds" do
51
+ fb = buffer(point(4,4))
52
+ fb.fill :area => rect(0,-4,1,1), :string => "-", :bg=>color(0,0,0)
53
+ fb.to_s.should == " \n \n \n "
54
+ end
55
+
50
56
  it "cropped fill" do
51
57
  (f=test_frame).cropped(rect(1,1,2,1)) do
52
58
  f.fill :string => '-'
@@ -166,5 +172,55 @@ describe "Buffer" do
166
172
  f1.fg_buffer.should == [[7, 7, 7, 7], [7, 7, 7, 7], [7, 7, 7, 7], [7, 7, 7, 7]]
167
173
  f1.bg_buffer.should == [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
168
174
  end
175
+
176
+ it "sanitize_contents" do
177
+ f1 = test_frame
178
+ f1.contents = "hi\t!\nthere"
179
+ f1.to_s.should == "hi?!\nther\n \n "
180
+ f1.contents[1]="boo\x00"
181
+ f1.to_s.should == "hi?!\nboo\x00\n \n "
182
+ f1.sanitize_contents 2..-1
183
+ f1.to_s.should == "hi?!\nboo\x00\n \n "
184
+ f1.sanitize_contents 1..1
185
+ f1.to_s.should == "hi?!\nboo?\n \n "
186
+ end
187
+
188
+ it "each_line" do
189
+ f1 = test_frame
190
+ f1.each_line.collect{|a|a}.should == [
191
+ ["1234", [7, 7, 7, 7], [0, 0, 0, 0]],
192
+ ["2345", [7, 7, 7, 7], [0, 0, 0, 0]],
193
+ ["3456", [7, 7, 7, 7], [0, 0, 0, 0]],
194
+ ["4567", [7, 7, 7, 7], [0, 0, 0, 0]]
195
+ ]
196
+ end
197
+
198
+ it "fg_buffer=" do
199
+ f1 = test_frame
200
+ f1.fg_buffer = [[8],[6,7,8]]
201
+ f1.fg_buffer.should == [[8, 7, 7, 7], [6, 7, 8, 7], [7, 7, 7, 7], [7, 7, 7, 7]]
202
+ f1.bg_buffer.should == [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
203
+ end
204
+
205
+ it "bg_buffer=" do
206
+ f1 = test_frame
207
+ f1.bg_buffer = [[8],[6,7,8]]
208
+ f1.fg_buffer.should == [[7, 7, 7, 7], [7, 7, 7, 7], [7, 7, 7, 7], [7, 7, 7, 7]]
209
+ f1.bg_buffer.should == [[8, 0, 0, 0], [6, 7, 8, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
210
+ end
211
+
212
+ it "normalize range" do
213
+ f1 = buffer(size = point(2,4))
214
+ f1.fill bg:255
215
+ f1.bg_buffer.should == [[255,255],[255,255],[255,255],[255,255]]
216
+ f1.normalize 1..2
217
+ f1.bg_buffer.should == [[255,255],[255,255],[255,255],[255,255]]
218
+ f1.normalize 1...2
219
+ f1.bg_buffer.should == [[255,255],[255,255],[255,255],[255,255]]
220
+ f1.size.should == size
221
+ f1.contents.length.should == size.y
222
+ f1.fg_buffer.length.should == size.y
223
+ f1.bg_buffer.length.should == size.y
224
+ end
169
225
  end
170
226
  end
@@ -32,5 +32,19 @@ describe "Color" do
32
32
  color("#abc").to_hex.should == "#aabbcc"
33
33
  color("#abcdef").to_hex.should == "#abcdef"
34
34
  end
35
+
36
+ it "br" do
37
+ color("#fff").br.should == 1.0
38
+ color("#000").br.should == 0.0
39
+ (color("#ff7f00").br*100).to_i.should == 49
40
+ end
41
+
42
+ it "to_screen_color" do
43
+ color("#ff0").to_screen_color.should == 226
44
+ color("#011").to_screen_color.should == 16
45
+ color("#777").to_screen_color.should == 243
46
+ color("#000").to_screen_color.should == 0
47
+ color("#ffffff").to_screen_color.should == 15
48
+ end
35
49
  end
36
50
  end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ module WindowBlessing
4
+ describe "EventManager" do
5
+ include Tools
6
+
7
+ it "inspect" do
8
+ em = EventManager.new "a parent"
9
+ em.inspect["EventManager"].should == "EventManager"
10
+ end
11
+
12
+ it "custom event" do
13
+ em = EventManager.new "a parent"
14
+ val = 0
15
+ em.on(:custom_event) {|event|val = event[:val]}
16
+ val.should == 0
17
+ em.handle_event type: :custom_event, :val => 1
18
+ val.should == 1
19
+ end
20
+
21
+ it "on_event_exception" do
22
+ em = EventManager.new "a parent"
23
+ exception_count = 0
24
+ em.on(:event_exception) {exception_count+=1}
25
+ em.on(:custom_event) {raise "foo"}
26
+ em.handle_event type: :custom_event
27
+ exception_count.should == 1
28
+ end
29
+
30
+ it "on_every_event" do
31
+ em = EventManager.new "a parent"
32
+ event_count = 0
33
+ em.on {event_count+=1}
34
+ em.handle_event type: :custom_event
35
+ em.handle_event type: :a_different_custom_event
36
+ event_count.should == 2
37
+ end
38
+
39
+ it "on_unhandled_event" do
40
+ em = EventManager.new "a parent"
41
+ handled = ""
42
+
43
+ em.on(:custom_event) {handled+="a"}
44
+ em.on(:unhandled_event) {handled+="b"}
45
+
46
+ em.handle_event type: :custom_event
47
+ em.handle_event type: :a_different_custom_event
48
+ em.handle_event type: :b_different_custom_event
49
+ em.handle_event type: :custom_event
50
+ handled.should == "abba"
51
+ end
52
+
53
+ it "exception in on_event_exception handler" do
54
+ em = EventManager.new "a parent"
55
+ handled = ""
56
+
57
+ em.on(:event_exception) {handled+="e";raise "foo"}
58
+ em.on(:custom_event) {handled+="c";raise "foo"}
59
+
60
+ em.handle_event type: :custom_event
61
+ handled.should == "ce"
62
+ end
63
+
64
+ it "on_last" do
65
+ em = EventManager.new "a parent"
66
+ handled = ""
67
+
68
+ em.on(:custom_event) {handled += "a"}
69
+ em.on(:custom_event) {handled += "b"}
70
+ em.on_last(:custom_event) {handled += "!"}
71
+
72
+ em.handle_event type: :custom_event
73
+ handled.should == "ba!"
74
+ end
75
+
76
+ it "handle_events" do
77
+ em = EventManager.new "a parent"
78
+ handled = ""
79
+ em.on(:custom_event) {handled += "a"}
80
+ em.handle_events [{type: :custom_event},{type: :other_custom_event},{type: :custom_event}]
81
+ handled.should == "aa"
82
+ end
83
+
84
+ it "layered event types" do
85
+ em = EventManager.new "a parent"
86
+ handled = ""
87
+ em.on(:custom_event) {handled += "C"}
88
+ em.on(:custom_event, :sub_type_a) {handled += "ca"}
89
+ em.on(:custom_event, :sub_type_b) {handled += "cb"}
90
+
91
+ em.handle_event :type => :custom_event
92
+ handled.should == "C"
93
+
94
+ handled = ""
95
+ em.handle_event :type => [:custom_event]
96
+ handled.should == "C"
97
+
98
+ handled = ""
99
+ em.handle_event :type => [:custom_event, :sub_type_a]
100
+ handled.should == "caC"
101
+
102
+ handled = ""
103
+ em.handle_event :type => [:custom_event, :sub_type_b]
104
+ handled.should == "cbC"
105
+ end
106
+ end
107
+ end