windows 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ module Windows
2
+ module Units
3
+ module Recognizer
4
+ module Formats
5
+ class Base < Struct.new(:el)
6
+ attr_accessor :unit
7
+
8
+ def match
9
+ raise "you should implement your match"
10
+ end
11
+
12
+ def format
13
+ self.class.to_s.split("::").last.downcase.to_sym
14
+ end
15
+
16
+ def return_match(matcher, data,&block)
17
+ data = if data
18
+ matcher.instance_eval &block
19
+ matcher
20
+ else
21
+ nil
22
+ end
23
+ end
24
+ end
25
+
26
+ class Pixel < Base
27
+ # match 50, 50.5, 50.02
28
+ def match
29
+ data = el.kind_of?(Numeric)
30
+ item = return_match(self, data) { @unit = el }
31
+ end
32
+ end
33
+
34
+ class Percent < Base
35
+ # match 50px, 50PX, 50.5px, 50.1PX
36
+ def match
37
+ return false unless el.respond_to?(:match)
38
+
39
+ data = el.match(/(^[\d.]+)(%)/i)
40
+ return_match(self, data) { @unit = Float(data[1]) }
41
+ end
42
+ end
43
+
44
+ RECOGNIZERS = [Pixel, Percent]
45
+ end
46
+
47
+ class Finder < Struct.new(:el)
48
+ include Formats
49
+
50
+ def run
51
+ item = RECOGNIZERS.map do |klass|
52
+ recognizer = klass.new(el)
53
+ recognizer.match
54
+ end.compact.first
55
+
56
+ raise "I don't know how to recognize this unit #{el}" unless item
57
+ item
58
+ end
59
+ end
60
+
61
+ def recognize_unit(el)
62
+ @recognize_unit_engine ||= Finder.new
63
+ @recognize_unit_engine.el = el
64
+ @recognize_unit_engine.run
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,73 @@
1
+ require 'windows/units/converter'
2
+ require 'windows/units/recognizer'
3
+ require 'windows/structures'
4
+
5
+ module Windows
6
+ module Units
7
+ class UnitConverter
8
+ include Units::Converter
9
+ include Units::Recognizer
10
+ include Structures
11
+
12
+ NAMED_GEOMETRY = {
13
+ left: [0, 0, '50%', '100%'],
14
+ right: ['50%', 0, '50%', '100%'],
15
+ bottom: [0, '50%', '100%', '50%'],
16
+ top: [0, 0, '100%', '50%'],
17
+ max: [0, 0, '100%', '100%'],
18
+ bottom_right: ['50%', '50%', '50%', '50%'],
19
+ bottom_left: [0, '50%', '50%', '50%'],
20
+ top_right: ['50%', 0, '50%', '50%'],
21
+ top_left: [0, 0, '50%', '50%']
22
+ }
23
+
24
+ def initialize(desktop, *args)
25
+ @width = desktop.width
26
+ @height = desktop.height
27
+ @geometry = create_geometry(args)
28
+ @x_offset = desktop.x_offset
29
+ @y_offset = desktop.y_offset
30
+ @x_axis = [@geometry.x, @geometry.w]
31
+ @y_axis = [@geometry.y, @geometry.h]
32
+ end
33
+
34
+ def convert
35
+ x, w = @x_axis.map do |el|
36
+ item = recognize_unit(el)
37
+ converter(item.unit).to(item.format, base: @width)
38
+ end
39
+
40
+ y, h = @y_axis.map do |el|
41
+ item = recognize_unit(el)
42
+ converter(item.unit).to(item.format, base: @height)
43
+ end
44
+
45
+ x = x + @x_offset
46
+ y = y + @y_offset
47
+
48
+ [x, y, w, h]
49
+ end
50
+
51
+ private
52
+
53
+ def create_geometry(args)
54
+ return Geometry.new(*args) if args.count > 1
55
+
56
+ if args[0].respond_to?(:to_sym) && geometry_exist?(args[0])
57
+ args = find_geometry(args[0].to_sym)
58
+ return Geometry.new(*args)
59
+ else
60
+ raise "Geometry with name #{args[0]} not exist. You can use #{NAMED_GEOMETRY.keys}"
61
+ end
62
+ end
63
+
64
+ def find_geometry(name)
65
+ NAMED_GEOMETRY[name.to_sym]
66
+ end
67
+
68
+ def geometry_exist?(name)
69
+ NAMED_GEOMETRY.keys.include?(name.to_sym)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ module Windows
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__),'../lib'))
2
+ require 'windows'
3
+
4
+ terminal1 = Windows::Window.new('x-www-browser')
5
+ terminal2 = Windows::Window.new('x-www-browser')
6
+ terminal3 = Windows::Window.new('x-www-browser')
7
+ windows = [terminal1, terminal2, terminal3]
8
+
9
+ # first create all windows
10
+ windows.each(&:create)
11
+
12
+ # move with percentage values
13
+ terminal1.move '50%', '50%', '50%', '50%'
14
+
15
+ # move with special named argument
16
+ terminal2.move :top_right
17
+
18
+ # move with pixels
19
+ terminal3.move 0, 0, 300, 300
20
+
21
+ sleep 5
22
+ windows.each(&:close)
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift lib = File.expand_path(File.join(File.dirname(__FILE__),'../lib'))
2
+ require 'windows'
3
+
4
+ p = Windows::Project.new(:default,lib)
5
+
6
+ p.open_window('x-www-browser').move(:right)
7
+ p.open_window('x-terminal-emulator').move(:bottom)
8
+
9
+ sleep 5
10
+ p.close
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'windows/core_ext/numeric'
3
+
4
+ describe Numeric do
5
+ context "#percent" do
6
+ it 'return correct value' do
7
+ 1000.percent(10).should == 100
8
+ end
9
+
10
+ it 'floor result' do
11
+ 999.percent(31.53).should == 314
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+ require 'windows/engines/wmctrl'
3
+ require 'time'
4
+
5
+ describe WMCtrl do
6
+ let(:fake_window_id) { 1234 }
7
+ let(:window) { Windows::Structures::Window.new(fake_window_id) }
8
+ let(:time) { Time.parse("Sep 13 2011") }
9
+ let(:command) { 'any_command' }
10
+
11
+ before :each do
12
+ create_windows
13
+ create_desktops
14
+ end
15
+
16
+ context "spawn_window" do
17
+ it 'should raise exception when command not exist' do
18
+ Process.stub(:spawn).and_raise(Errno::ENOENT)
19
+ expect { subject.spawn_window(command) }.to raise_error "Failed to create window with command: #{command}. Maybe a typo?"
20
+ end
21
+ end
22
+
23
+ it "#desktops" do
24
+ list = subject.desktops
25
+ desktops.zip(list).each do |desktop, desktop_window|
26
+ desktop[:id].should == desktop_window.id
27
+ desktop[:workarea].should == desktop_window.geometry
28
+ end
29
+
30
+ list.should be_instance_of(Windows::Structures::Collection)
31
+ end
32
+
33
+ it "#find_desktops" do
34
+ desktop = subject.find_desktop(1)
35
+ desktop.id.should == 1
36
+ desktop.should be_instance_of(Windows::Structures::Desktop)
37
+ end
38
+
39
+ context "#windows" do
40
+ it 'return windows' do
41
+ list = subject.windows
42
+
43
+ windows(:sorted).zip(list).each do |stub, window|
44
+ desktop = subject.find_desktop(stub[:desktop])
45
+
46
+ stub[:id].should == window.id
47
+ stub[:title].should == window.title
48
+ stub[:desktop].should == desktop.id
49
+ stub[:geometry][0].should == window.x
50
+ stub[:geometry][1].should == window.y
51
+ stub[:geometry][2].should == window.width
52
+ stub[:geometry][3].should == window.height
53
+ end
54
+
55
+ list.should be_instance_of(Windows::Structures::Collection)
56
+ end
57
+
58
+ it 'return sorted by :id' do
59
+ expected_ids = windows(:sorted).map {|w| w[:id]}
60
+
61
+ subject.windows.ids.should == expected_ids
62
+ end
63
+ end
64
+
65
+ context "#find_windows" do
66
+ it 'should find window by id' do
67
+ subject.find_window(2).id.should == 2
68
+ end
69
+
70
+ it 'should return Window' do
71
+ subject.find_window(2).should be_instance_of Windows::Structures::Window
72
+ end
73
+ end
74
+
75
+ it '#active_window' do
76
+ subject.active_window.active.should be_true
77
+ end
78
+
79
+ private
80
+
81
+ def desktops
82
+ unless @desktops
83
+ klass = Windows::Structures::Desktop
84
+ @desktops = []
85
+ @desktops.push({id: 1, workarea: [0, 0, 800, 600]})
86
+ @desktops.push({id: 2, workarea: [0, 0, 1200, 1000]})
87
+ end
88
+ @desktops
89
+ end
90
+
91
+ def windows(sorted = false)
92
+ unless @windows
93
+ klass = Windows::Structures::Window
94
+ @windows = []
95
+ @windows.push({id: 1, title: 'bash', desktop: 1, geometry: [0,0,100,200]})
96
+ @windows.push({id: 3, title: 'torr', desktop: 2, geometry: [100,100,400,800]})
97
+ @windows.push({id: 2, title: 'list', desktop: 1, geometry: [50,50,200,400], active: true})
98
+ end
99
+ sorted ? @windows.sort_by{|w| w[:id]} : @windows
100
+ end
101
+
102
+ def create_windows
103
+ subject.stub(:list_windows).and_return(windows)
104
+ end
105
+
106
+ def create_desktops
107
+ subject.stub(:list_desktops).and_return(desktops)
108
+ end
109
+
110
+ end
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+ require 'time'
3
+ require 'windows/engines/xwindow'
4
+ require 'windows/structures/window'
5
+ require 'windows/structures/desktop'
6
+
7
+ module Windows
8
+ module Engines
9
+ class WMCtrl
10
+ end
11
+ end
12
+ end
13
+
14
+ class DummyEngine
15
+ def action(*args)
16
+ end
17
+
18
+ def spawn_window(command)
19
+ fake_window
20
+ end
21
+
22
+ def find_window(id)
23
+ fake_window
24
+ end
25
+
26
+ def fake_window
27
+ Windows::Structures::Window.new(1234, 'fake window', fake_desktop)
28
+ end
29
+
30
+ private
31
+
32
+ def fake_desktop
33
+ Windows::Structures::Desktop.new(9876, [0,0,800,600])
34
+ end
35
+ end
36
+ class DummyWindow < Struct.new(:id, :title);end
37
+ class DummyDesktop < Struct.new(:x_offset, :y_offset, :width, :height);end
38
+
39
+ describe Windows::Engines::XWindow do
40
+ subject { Windows::Engines::XWindow.new(command, options, engine)}
41
+ let(:engine) { DummyEngine.new }
42
+ let(:command) { 'ls' }
43
+ let(:time) { Time.parse("Sep 13 2011")}
44
+ let(:options) { Hash.new }
45
+ let(:window) { engine.fake_window }
46
+ let(:id) { window.id }
47
+
48
+ it { should delegate(:title).to(:window) }
49
+ it { should delegate(:desktop).to(:window) }
50
+ it { should delegate(:x).to(:window) }
51
+ it { should delegate(:y).to(:window) }
52
+ it { should delegate(:width).to(:window) }
53
+ it { should delegate(:height).to(:window) }
54
+ it { should respond_to :command= }
55
+
56
+ it '#move' do
57
+ args = [100, 200, 500, 400]
58
+
59
+ subject.should_receive(:undock).ordered
60
+ engine.should_receive(:action).with(id, :move_resize, 0, *args).ordered
61
+ subject.move(*args)
62
+ end
63
+
64
+ context 'close' do
65
+ it 'should delegate to engine' do
66
+ engine.should_receive(:action).with(id, :close)
67
+ subject.close
68
+ end
69
+
70
+ it 'should :id, :created_at set to false' do
71
+ subject.create
72
+ subject.close
73
+ subject.id.should == false
74
+ subject.created_at.should == false
75
+ end
76
+ end
77
+
78
+ it '#focus' do
79
+ engine.should_receive(:action).with(id, :activate)
80
+ subject.focus
81
+ end
82
+
83
+ it '#undock' do
84
+ args = ["remove", "maximized_vert", "maximized_horz"]
85
+
86
+ engine.should_receive(:action).with(id, :change_state, *args)
87
+ subject.undock
88
+ end
89
+
90
+ context "#create" do
91
+ before(:each) do
92
+ Time.stub!(:now).and_return(time)
93
+ end
94
+
95
+ it 'should assign id' do
96
+ subject.create
97
+ subject.id.should == engine.fake_window.id
98
+ end
99
+
100
+ it 'should assign created_at' do
101
+ subject.create
102
+ subject.created_at.should == time
103
+ end
104
+
105
+ it 'should return false when window is already created' do
106
+ subject.create
107
+ subject.create.should be_false
108
+ end
109
+
110
+ it 'should not spawn new window when it exist' do
111
+ engine.should_receive(:find_window).with(command).and_return(window)
112
+ engine.should_not_receive(:spawn_window).with(command)
113
+ subject.create
114
+ end
115
+ end
116
+
117
+ context '#window' do
118
+ context "when window is not created" do
119
+ it 'should create window' do
120
+ subject.should_receive(:create)
121
+ subject.window
122
+ end
123
+ end
124
+
125
+ context "when window is created" do
126
+ before do
127
+ subject.create
128
+ end
129
+
130
+ it 'should delegate to engine' do
131
+ engine.should_receive(:find_window).with(id)
132
+ subject.window
133
+ end
134
+ end
135
+ end
136
+
137
+ it '#on_top' do
138
+ engine.should_receive(:action).with(id, :change_state, "add", "above")
139
+ subject.on_top
140
+ end
141
+
142
+ it '#not_on_top' do
143
+ engine.should_receive(:action).with(id, :change_state, "remove", "above")
144
+ subject.not_on_top
145
+ end
146
+
147
+ context "#action" do
148
+ it 'should delegate to engine' do
149
+ args = [0, 0, 0, 100, 200]
150
+
151
+ engine.should_receive(:action).with(id, *args)
152
+ subject.action(*args)
153
+ end
154
+
155
+ it 'should return self' do
156
+ subject.action.should == subject
157
+ end
158
+ end
159
+
160
+ it '#maximize' do
161
+ engine.should_receive(:action).with(id, :change_state, "add", "maximized_vert", "maximized_horz")
162
+ subject.maximize
163
+ end
164
+
165
+ context "initialize" do
166
+ it "should use WMCtrl as default engine" do
167
+ object = subject.class.new(command, options, nil)
168
+ object.engine.should be_instance_of(Windows::Engines::WMCtrl)
169
+ end
170
+ end
171
+ end