window_blessing 0.0.1 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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