windows 0.0.1

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.
@@ -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