uh-layout 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 13e0b24b7af337b1124358fdd2719f9489d753c4
4
- data.tar.gz: c1cc1e138a983f9b32e5237123665ebe57382dfd
3
+ metadata.gz: 47a100f2229b06ab1aa01eeb35e1b8baba73f804
4
+ data.tar.gz: 3cc639552ca1d7cfa766a0d6afb03dab85532b86
5
5
  SHA512:
6
- metadata.gz: 332e7d81affd715faf55fd6f0b424da82b7b0852907052a228c8017458c5a25ae8488ff1c2b3afa0c5c62abfef11ab72c6538557542204132b3c88d515d4dfe2
7
- data.tar.gz: 327f4d842b1e3cd74cfb818991ee0c71ee1777d5767752d6415a1d59a90f041fa0e59af7241fb3df196a2ad166c6232d44e9117c74c0ee3efdbe5377090ea10c
6
+ metadata.gz: 2c0c3f914c932e129fb9eaf10c18a6fe7cc90335adec8f4822aecda6a6856204ceff06e832016d59716de7b08506450f49b0c56c36b53198d6e29bce3a31f621
7
+ data.tar.gz: 8a7ce44e77e6edad1746dc478253a0a742c404538e0b9d771e605ad67a6ecc6c19843a1f984cdec1ed4841d697a467574134aa6568d7105edcbe726f7943328c
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
+ /Gemfile-custom.rb
1
2
  /Gemfile.lock
2
3
  /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0
4
+ - 2.1
5
+ - 2.2
6
+ - ruby-head
7
+ - rbx-2
@@ -0,0 +1,30 @@
1
+ module Uh
2
+ class Layout
3
+ module Arrangers
4
+ class FixedWidth
5
+ DEFAULT_WIDTH = 484
6
+
7
+ def initialize(entries, geo, width: DEFAULT_WIDTH)
8
+ @entries = entries
9
+ @geo = geo
10
+ @width = width
11
+ end
12
+
13
+ def arrange
14
+ return if @entries.empty?
15
+ @entries.each_with_index do |column, i|
16
+ column.x = @width * i + @geo.x
17
+ column.y = @geo.y
18
+ column.width = @width
19
+ column.height = @geo.height
20
+ end
21
+ @entries.last.width = @geo.width - (@entries.last.x - @geo.x)
22
+ end
23
+
24
+ def max_count?
25
+ (@geo.width / (@entries.size + 1)) < @width
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ module Uh
2
+ class Layout
3
+ module Arrangers
4
+ class Stack
5
+ def initialize(entries, geo)
6
+ @entries = entries
7
+ @geo = geo
8
+ end
9
+
10
+ def arrange
11
+ @entries.each { |e| e.geo = @geo.dup }
12
+ end
13
+
14
+ def each_visible
15
+ yield @entries.current if @entries.current
16
+ end
17
+
18
+ def each_hidden
19
+ ([*@entries] - [@entries.current]).each { |e| yield e }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ module Uh
2
+ class Layout
3
+ module Arrangers
4
+ class VertTile
5
+ def initialize(entries, geo)
6
+ @entries = entries
7
+ @geo = geo
8
+ end
9
+
10
+ def arrange
11
+ entry_height = @geo.height / @entries.size - 1
12
+ @entries.each_with_index do |entry, i|
13
+ entry.geo = @geo.dup
14
+ entry.y = (entry_height + 1) * i
15
+ entry.height = entry_height
16
+ end
17
+ end
18
+
19
+ def each_visible
20
+ @entries.each { |e| yield e }
21
+ end
22
+
23
+ def each_hidden
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ module Uh
2
+ class Layout
3
+ class ClientColumnMover
4
+ def initialize(columns, columns_max_count)
5
+ @columns = columns
6
+ @columns_max_count = columns_max_count
7
+ end
8
+
9
+ def move_current(direction)
10
+ @columns.current.remove client = @columns.current.current_client
11
+ dest_column = get_or_create_column direction
12
+ dest_column << client
13
+ dest_column.current_client = client
14
+ @columns.current = dest_column
15
+ end
16
+
17
+ def get_or_create_column(direction)
18
+ if candidate = @columns.get(direction)
19
+ candidate
20
+ elsif @columns_max_count
21
+ @columns.get direction, cycle: true
22
+ else
23
+ Column.new(Geo.new).tap do |o|
24
+ case direction
25
+ when :pred then @columns.unshift o
26
+ when :succ then @columns << o
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,6 +1,11 @@
1
1
  module Uh
2
2
  class Layout
3
3
  class Column
4
+ MODES = {
5
+ stack: Arrangers::Stack,
6
+ tile: Arrangers::VertTile
7
+ }.freeze
8
+
4
9
  include GeoAccessors
5
10
 
6
11
  extend Forwardable
@@ -9,11 +14,12 @@ module Uh
9
14
  def_delegator :@clients, :current=, :current_client=
10
15
  def_delegator :current_client, :==, :current_client?
11
16
 
12
- attr_reader :geo, :clients
17
+ attr_reader :geo, :clients, :mode
13
18
 
14
19
  def initialize(geo)
15
20
  @geo = geo.dup
16
21
  @clients = Container.new
22
+ @mode = :stack
17
23
  end
18
24
 
19
25
  def to_s
@@ -26,18 +32,22 @@ module Uh
26
32
  self
27
33
  end
28
34
 
35
+ def mode_toggle
36
+ @mode = MODES.keys[(MODES.keys.index(@mode) + 1) % MODES.keys.size]
37
+ end
38
+
39
+ def arranger
40
+ MODES[@mode].new @clients, @geo
41
+ end
42
+
29
43
  def arrange_clients
30
- @clients.each do |client|
31
- client.geo = @geo.dup
32
- client.moveresize
33
- end
44
+ arranger.arrange
45
+ clients.each &:moveresize
34
46
  end
35
47
 
36
- def show_hide_clients
37
- @clients.each do |client|
38
- client.hide unless client.hidden? || @clients.current == client
39
- end
40
- @clients.current.show if @clients.current && @clients.current.hidden?
48
+ def show_hide_clients(arranger: self.arranger)
49
+ arranger.each_visible { |client| client.show if client.hidden? }
50
+ arranger.each_hidden { |client| client.hide unless client.hidden? }
41
51
  end
42
52
  end
43
53
  end
@@ -4,8 +4,8 @@ module Uh
4
4
  include Enumerable
5
5
 
6
6
  extend Forwardable
7
- def_delegators :@entries, :<<, :[], :each, :empty?, :first, :index, :last,
8
- :size, :unshift
7
+ def_delegators :@entries, :<<, :[], :each, :empty?, :fetch, :first,
8
+ :index, :last, :size, :unshift
9
9
 
10
10
  def initialize(entries = [])
11
11
  @entries = entries
@@ -19,7 +19,8 @@ module Uh
19
19
  end
20
20
 
21
21
  def current=(entry)
22
- @current_index = @entries.index entry if include? entry
22
+ fail ArgumentError, 'unknown entry' unless include? entry
23
+ @current_index = @entries.index entry
23
24
  end
24
25
 
25
26
  def remove(entry)
data/lib/uh/layout/tag.rb CHANGED
@@ -9,6 +9,7 @@ module Uh
9
9
  def_delegator :@columns, :each, :each_column
10
10
  def_delegator :current_column, :==, :current_column?
11
11
  def_delegator :clients, :each, :each_client
12
+ def_delegator :arranger, :max_count?, :columns_max_count?
12
13
 
13
14
  attr_reader :id, :geo, :columns
14
15
 
@@ -39,6 +40,16 @@ module Uh
39
40
  end
40
41
  end
41
42
 
43
+ def arranger
44
+ Arrangers::FixedWidth.new(@columns, @geo)
45
+ end
46
+
47
+ def arrange_columns
48
+ @columns.remove_if &:empty?
49
+ arranger.arrange
50
+ @columns.each &:arrange_clients
51
+ end
52
+
42
53
  def hide
43
54
  clients.each &:hide
44
55
  end
@@ -1,5 +1,5 @@
1
1
  module Uh
2
2
  class Layout
3
- VERSION = '0.1.2'
3
+ VERSION = '0.1.3'
4
4
  end
5
5
  end
data/lib/uh/layout.rb CHANGED
@@ -1,16 +1,21 @@
1
1
  require 'forwardable'
2
2
 
3
- require 'uh/layout/geo_accessors'
3
+ require 'uh/layout/arrangers/fixed_width'
4
+ require 'uh/layout/arrangers/stack'
5
+ require 'uh/layout/arrangers/vert_tile'
4
6
  require 'uh/layout/bar'
7
+ require 'uh/layout/client_column_mover'
5
8
  require 'uh/layout/container'
6
9
  require 'uh/layout/column'
7
- require 'uh/layout/column/arranger'
8
10
  require 'uh/layout/dumper'
9
11
  require 'uh/layout/screen'
10
12
  require 'uh/layout/tag'
11
13
 
12
14
  module Uh
13
15
  class Layout
16
+ Error = Class.new(StandardError)
17
+ ArgumentError = Class.new(Error)
18
+
14
19
  extend Forwardable
15
20
  def_delegator :@screens, :current, :current_screen
16
21
  def_delegator :current_screen, :==, :current_screen?
@@ -36,10 +41,6 @@ module Uh
36
41
  screens.any? { |screen| screen.include? client }
37
42
  end
38
43
 
39
- def arranger_for_current_tag
40
- Column::Arranger.new(current_tag.columns, current_tag.geo)
41
- end
42
-
43
44
  def update_widgets
44
45
  @widgets.each &:update
45
46
  @widgets.each &:redraw
@@ -63,8 +64,7 @@ module Uh
63
64
  def remove(client)
64
65
  screen, tag, column = find_client client
65
66
  column.remove client
66
- Column::Arranger.new(tag.columns, tag.geo).redraw
67
- tag.each_column &:arrange_clients
67
+ tag.arrange_columns
68
68
  column.show_hide_clients
69
69
  current_client.focus if current_client
70
70
  update_widgets
@@ -94,14 +94,11 @@ module Uh
94
94
 
95
95
  def handle_tag_set(tag_id)
96
96
  return unless current_client && current_tag.id != tag_id
97
+ previous_tag_id = current_tag.id
97
98
  remove client = current_client
98
- client.hide
99
- tag = find_tag_or_create tag_id
100
- tag.current_column_or_create << client
101
- Column::Arranger.new(tag.columns, tag.geo).redraw
102
- tag.each_column &:arrange_clients
103
- current_client.focus if current_client
104
- update_widgets
99
+ handle_tag_sel tag_id
100
+ push client
101
+ handle_tag_sel previous_tag_id
105
102
  end
106
103
 
107
104
  def handle_column_sel(direction)
@@ -111,6 +108,13 @@ module Uh
111
108
  update_widgets
112
109
  end
113
110
 
111
+ def handle_column_mode_toggle
112
+ return unless current_column
113
+ current_column.mode_toggle
114
+ current_column.arrange_clients
115
+ current_column.show_hide_clients
116
+ end
117
+
114
118
  def handle_client_sel(direction)
115
119
  return unless current_client
116
120
  current_column.clients.sel direction
@@ -125,10 +129,10 @@ module Uh
125
129
  update_widgets
126
130
  end
127
131
 
128
- def handle_client_column_set(direction, arranger: arranger_for_current_tag)
132
+ def handle_client_column_set(direction, mover: client_mover_for_current_tag)
129
133
  return unless current_client
130
- arranger.move_current_client(direction).update_geos
131
- current_tag.each_column &:arrange_clients
134
+ mover.move_current direction
135
+ current_tag.arrange_columns
132
136
  current_tag.each_column &:show_hide_clients
133
137
  update_widgets
134
138
  end
@@ -157,5 +161,9 @@ module Uh
157
161
  current_screen.tags << tag
158
162
  end
159
163
  end
164
+
165
+ def client_mover_for_current_tag
166
+ ClientColumnMover.new(current_tag.columns, current_tag.columns_max_count?)
167
+ end
160
168
  end
161
169
  end
@@ -1,8 +1,22 @@
1
1
  module Factories
2
+ class Entry
3
+ include Uh::GeoAccessors
4
+
5
+ attr_accessor :geo
6
+
7
+ def initialize(geo)
8
+ @geo = geo
9
+ end
10
+ end
11
+
2
12
  def build_client
3
13
  Uh::WM::Client.new(instance_spy Uh::Window)
4
14
  end
5
15
 
16
+ def build_entry(geo = build_geo)
17
+ Entry.new(geo)
18
+ end
19
+
6
20
  def build_geo(x = 0, y = 0, width = 640, height = 480)
7
21
  Uh::Geo.new(x, y, width, height)
8
22
  end
@@ -0,0 +1,72 @@
1
+ module Uh
2
+ class Layout
3
+ module Arrangers
4
+ describe FixedWidth do
5
+ let(:geo) { build_geo 20, 0, 640, 480 }
6
+ let(:entry) { build_entry }
7
+ let(:entries) { Container.new([entry, build_entry]) }
8
+ subject(:arranger) { described_class.new entries, geo, width: 300 }
9
+
10
+ describe '#arrange' do
11
+ it 'decreases first entry width as the optimal width' do
12
+ arranger.arrange
13
+ expect(entries[0].width).to eq 300
14
+ end
15
+
16
+ it 'offsets entries with given geo' do
17
+ arranger.arrange
18
+ expect(entries[0].x).to eq 20
19
+ end
20
+
21
+ it 'moves second entry aside the first entry' do
22
+ arranger.arrange
23
+ expect(entries[1].x).to eq 320
24
+ end
25
+
26
+ it 'increases last entry width to occupy remaining width' do
27
+ arranger.arrange
28
+ expect(entries[1].width).to eq 340
29
+ end
30
+
31
+ it 'copies given geo y' do
32
+ entries[0].y = nil
33
+ arranger.arrange
34
+ expect(entries[0].y).to eq 0
35
+ end
36
+
37
+ it 'copies given geo height' do
38
+ entries[0].height = nil
39
+ arranger.arrange
40
+ expect(entries[0].height).to eq 480
41
+ end
42
+
43
+ context 'without entry' do
44
+ let(:entries) { Container.new([]) }
45
+
46
+ it 'does not raise any error' do
47
+ expect { arranger.arrange }.not_to raise_error
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#max_count?' do
53
+ context 'when a new entry fits in current geo' do
54
+ let(:entries) { Container.new([entry]) }
55
+
56
+ it 'returns false' do
57
+ expect(arranger.max_count?).to be false
58
+ end
59
+ end
60
+
61
+ context 'when current geo can not contain more entry' do
62
+ let(:entries) { Container.new([entry, entry.dup]) }
63
+
64
+ it 'returns true' do
65
+ expect(arranger.max_count?).to be true
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,42 @@
1
+ module Uh
2
+ class Layout
3
+ module Arrangers
4
+ describe Stack do
5
+ let(:entry) { build_entry }
6
+ let(:other_entry) { build_entry }
7
+ let(:entries) { Container.new([entry, other_entry]) }
8
+ let(:geo) { build_geo 0, 0, 300, 480 }
9
+ subject(:arranger) { described_class.new entries, geo }
10
+
11
+ describe '#arrange' do
12
+ it 'sets given geo on all entries' do
13
+ arranger.arrange
14
+ expect(entries.map(&:geo)).to all eq geo
15
+ end
16
+ end
17
+
18
+ describe '#each_visible' do
19
+ it 'yields current entry' do
20
+ expect { |b| arranger.each_visible &b }
21
+ .to yield_successive_args entry
22
+ end
23
+
24
+ context 'with no current entry' do
25
+ let(:entries) { Container.new([]) }
26
+
27
+ it 'does not yield' do
28
+ expect { |b| arranger.each_visible &b }.not_to yield_control
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '#each_hidden' do
34
+ it 'yields all entries except current one' do
35
+ expect { |b| arranger.each_hidden &b }
36
+ .to yield_successive_args other_entry
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ module Uh
2
+ class Layout
3
+ module Arrangers
4
+ describe VertTile do
5
+ let(:entry) { build_entry }
6
+ let(:other_entry) { build_entry }
7
+ let(:entries) { Container.new([entry, other_entry]) }
8
+ let(:geo) { build_geo 0, 0, 300, 480 }
9
+ subject(:arranger) { described_class.new entries, geo }
10
+
11
+ describe '#arrange' do
12
+ it 'sets x offset from given geo on all entries' do
13
+ arranger.arrange
14
+ expect(entries.map(&:x)).to all eq 0
15
+ end
16
+
17
+ it 'sets width from given geo on all entries' do
18
+ arranger.arrange
19
+ expect(entries.map(&:width)).to all eq 300
20
+ end
21
+
22
+ it 'splits entries height equally' do
23
+ arranger.arrange
24
+ expect(entries.map(&:height)).to all eq 239
25
+ end
26
+
27
+ it 'adds a margin between entries' do
28
+ arranger.arrange
29
+ expect(entries[1].y - entries[0].height - entries[0].y).to eq 1
30
+ end
31
+ end
32
+
33
+ describe '#each_visible' do
34
+ it 'yields all entries' do
35
+ expect { |b| arranger.each_visible &b }
36
+ .to yield_successive_args entry, other_entry
37
+ end
38
+ end
39
+
40
+ describe '#each_hidden' do
41
+ it 'yields no entry' do
42
+ expect { |b| arranger.each_hidden &b }.not_to yield_control
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,96 @@
1
+ module Uh
2
+ class Layout
3
+ describe ClientColumnMover do
4
+ let(:client) { build_client }
5
+ let(:column) { Column.new(build_geo) }
6
+ let(:columns) { Container.new([column]) }
7
+ let(:columns_max_count) { false }
8
+ subject(:mover) { described_class.new columns, columns_max_count }
9
+
10
+ describe '#move_current' do
11
+ shared_examples 'moves current client' do |expected_column_index|
12
+ it 'removes current client from origin column' do
13
+ mover.move_current :succ
14
+ expect(column).not_to include client
15
+ end
16
+
17
+ it 'adds current client in the destination column' do
18
+ mover.move_current :succ
19
+ expect(columns[expected_column_index]).to include client
20
+ end
21
+
22
+ it 'updates destination column as the current one' do
23
+ mover.move_current :succ
24
+ expect(columns.current).to be columns[expected_column_index]
25
+ end
26
+
27
+ it 'preserves current client as the current one' do
28
+ expect { mover.move_current :succ }
29
+ .not_to change { columns.current.current_client }
30
+ end
31
+ end
32
+
33
+ context 'given one column with one client' do
34
+ before { column << client }
35
+
36
+ include_examples 'moves current client', 1
37
+ end
38
+
39
+ context 'given one column with many clients' do
40
+ before { column << client << client.dup }
41
+
42
+ include_examples 'moves current client', 1
43
+ end
44
+
45
+ context 'given two columns' do
46
+ let(:columns) { Container.new([column, Column.new(build_geo)]) }
47
+
48
+ before { columns[1] << client.dup }
49
+
50
+ context 'when origin column has many clients' do
51
+ before { column << client << client.dup }
52
+
53
+ include_examples 'moves current client', 1
54
+ end
55
+
56
+ context 'when origin column has one client' do
57
+ before { column << client }
58
+
59
+ include_examples 'moves current client', 1
60
+ end
61
+ end
62
+ end
63
+
64
+ describe '#get_or_create_column' do
65
+ let(:columns) { Container.new([column, Column.new(build_geo)]) }
66
+
67
+ it 'returns the consecutive column in given direction' do
68
+ expect(mover.get_or_create_column :succ).to be columns[1]
69
+ end
70
+
71
+ context 'when current column is last in given direction' do
72
+ before { columns.current = columns[1] }
73
+
74
+ context 'when max columns count is not reached' do
75
+ it 'appends a new column' do
76
+ expect(mover.get_or_create_column :succ).to be columns[2]
77
+ end
78
+
79
+ it 'prepends a new column' do
80
+ columns.current = columns[0]
81
+ expect(mover.get_or_create_column :pred).to be columns[0]
82
+ end
83
+ end
84
+
85
+ context 'when max columns count is reached' do
86
+ let(:columns_max_count) { true }
87
+
88
+ it 'returns the consecutive column in given direction' do
89
+ expect(mover.get_or_create_column :succ).to be columns[0]
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -5,7 +5,7 @@ module Uh
5
5
  let(:other_geo) { build_geo 640, 0, 320, 240 }
6
6
  let(:client) { build_client }
7
7
  let(:other_client) { build_client }
8
- subject(:column) { described_class.new(geo) }
8
+ subject(:column) { described_class.new geo }
9
9
 
10
10
  it 'has a copy to given geo' do
11
11
  expect(column.geo).to eq(geo).and not_be geo
@@ -15,6 +15,10 @@ module Uh
15
15
  expect(column).to be_empty
16
16
  end
17
17
 
18
+ it 'has :stack as default mode' do
19
+ expect(column.mode).to be :stack
20
+ end
21
+
18
22
  describe '#<<' do
19
23
  before { column << client }
20
24
 
@@ -31,13 +35,43 @@ module Uh
31
35
  end
32
36
  end
33
37
 
38
+ describe '#mode_toggle' do
39
+ it 'toggles mode from stack to tile' do
40
+ expect { column.mode_toggle }
41
+ .to change { column.mode }.from(:stack).to(:tile)
42
+ end
43
+
44
+ it 'toggles mode from tile to stack' do
45
+ column.mode_toggle
46
+ expect { column.mode_toggle }
47
+ .to change { column.mode }.from(:tile).to(:stack)
48
+ end
49
+
50
+ end
51
+
52
+ describe '#arranger' do
53
+ context 'when column mode is stack' do
54
+ it 'returns a stack arranger' do
55
+ expect(column.arranger).to be_an Arrangers::Stack
56
+ end
57
+ end
58
+
59
+ context 'when column mode is tile' do
60
+ it 'returns a vertical tile arranger' do
61
+ column.mode_toggle
62
+ expect(column.arranger).to be_an Arrangers::VertTile
63
+ end
64
+ end
65
+ end
66
+
34
67
  describe '#arrange_clients' do
35
68
  before { column << client << other_client }
36
69
 
37
- it 'updates clients geometries' do
38
- column.width = 320
70
+ it 'arranges clients' do
71
+ arranger = instance_spy Arrangers::Stack
72
+ allow(column).to receive(:arranger) { arranger }
73
+ expect(arranger).to receive :arrange
39
74
  column.arrange_clients
40
- expect(column.clients.map(&:geo)).to all eq column.geo
41
75
  end
42
76
 
43
77
  it 'moveresizes clients' do
@@ -47,18 +81,24 @@ module Uh
47
81
  end
48
82
 
49
83
  describe '#show_hide_clients' do
50
- before { column << client << other_client }
84
+ let(:arranger) { double 'arranger', each_visible: nil, each_hidden: nil }
51
85
 
52
- it 'shows current client only' do
86
+ it 'shows visible clients when they are hidden' do
87
+ allow(arranger).to receive(:each_visible)
88
+ .and_yield(client.hide)
89
+ .and_yield(other_client.show)
90
+ expect(client).to receive :show
53
91
  expect(other_client).not_to receive :show
54
- expect(client.hide).to receive :show
55
- column.show_hide_clients
92
+ column.show_hide_clients arranger: arranger
56
93
  end
57
94
 
58
- it 'hides non-current clients' do
59
- expect(client).not_to receive :hide
60
- expect(other_client.show).to receive :hide
61
- column.show_hide_clients
95
+ it 'hides hidden clients except those already hidden' do
96
+ allow(arranger).to receive(:each_hidden)
97
+ .and_yield(client.show)
98
+ .and_yield(other_client.hide)
99
+ expect(client).to receive :hide
100
+ expect(other_client).not_to receive :hide
101
+ column.show_hide_clients arranger: arranger
62
102
  end
63
103
  end
64
104
  end
@@ -42,9 +42,9 @@ module Uh
42
42
  end
43
43
 
44
44
  context 'when given argument is not an entry' do
45
- it 'does not change current entry' do
46
- expect { container.current = :baz }
47
- .not_to change { container.current }
45
+ it 'raises ArgumentError' do
46
+ expect { container.current = :unknown_entry }
47
+ .to raise_error Layout::ArgumentError
48
48
  end
49
49
  end
50
50
  end
@@ -91,10 +91,10 @@ module Uh
91
91
  end
92
92
 
93
93
  context 'when given entry is the only one' do
94
- let(:entries) { [:foo] }
94
+ let(:entries) { [:bar] }
95
95
 
96
96
  it 'has no more current entry' do
97
- container.remove :foo
97
+ container.remove :bar
98
98
  expect(container.current).to be nil
99
99
  end
100
100
  end
@@ -4,7 +4,7 @@ module Uh
4
4
  let(:geo) { build_geo }
5
5
  let(:other_geo) { build_geo 640, 0, 320, 240 }
6
6
  let(:client) { build_client }
7
- subject(:screen) { described_class.new(0, geo) }
7
+ subject(:screen) { described_class.new 0, geo }
8
8
 
9
9
  it 'has one default tag with id 1 assigned' do
10
10
  expect(screen.tags).to include an_object_having_attributes id: '1'
@@ -6,11 +6,12 @@ module Uh
6
6
  let(:client) { build_client }
7
7
  let(:other_client) { build_client }
8
8
  let(:column) { Column.new(geo) }
9
- subject(:tag) { described_class.new('1', geo) }
9
+ subject(:tag) { described_class.new '1', geo }
10
10
 
11
11
  describe '.new' do
12
12
  it 'raises error unless id converts to string' do
13
- expect { described_class.new(1, geo) }.to raise_error(ArgumentError)
13
+ expect { described_class.new 1, geo }
14
+ .to raise_error(Layout::ArgumentError)
14
15
  end
15
16
  end
16
17
 
@@ -40,7 +41,7 @@ module Uh
40
41
  end
41
42
 
42
43
  it 'returns the new column' do
43
- expect(tag.current_column_or_create).to eq tag.columns.current
44
+ expect(tag.current_column_or_create).to be tag.columns.fetch 0
44
45
  end
45
46
  end
46
47
 
@@ -57,6 +58,34 @@ module Uh
57
58
  end
58
59
  end
59
60
  end
61
+
62
+ describe '#arranger' do
63
+ it 'returns a fixed width arranger' do
64
+ expect(tag.arranger).to be_an Arrangers::FixedWidth
65
+ end
66
+ end
67
+
68
+ describe '#arrange_columns' do
69
+ before { tag.columns << column }
70
+
71
+ it 'purges empty columns' do
72
+ tag.arrange_columns
73
+ expect(tag.columns).to be_empty
74
+ end
75
+
76
+ it 'arranges columns' do
77
+ arranger = instance_spy Arrangers::FixedWidth
78
+ allow(tag).to receive(:arranger) { arranger }
79
+ expect(arranger).to receive :arrange
80
+ tag.arrange_columns
81
+ end
82
+
83
+ it 'arranges columns clients' do
84
+ column << client
85
+ expect(column).to receive :arrange_clients
86
+ tag.arrange_columns
87
+ end
88
+ end
60
89
  end
61
90
  end
62
91
  end
@@ -23,17 +23,6 @@ module Uh
23
23
  end
24
24
  end
25
25
 
26
- describe '#arranger_for_current_tag' do
27
- it 'returns an arranger for current tag columns and geo' do
28
- expect(layout.arranger_for_current_tag)
29
- .to respond_to(:update_geos)
30
- .and have_attributes(
31
- columns: layout.current_tag.columns,
32
- geo: layout.current_tag.geo
33
- )
34
- end
35
- end
36
-
37
26
  describe '#update_widgets' do
38
27
  it 'updates widgets' do
39
28
  expect(layout.widgets).to all receive :update
@@ -110,13 +99,8 @@ module Uh
110
99
  expect(layout).not_to include client
111
100
  end
112
101
 
113
- it 'redraws columns with an arranger' do
114
- expect_any_instance_of(Layout::Column::Arranger).to receive :redraw
115
- layout.remove client
116
- end
117
-
118
- it 'arranges clients in removed client tag columns' do
119
- expect(layout.current_tag.columns).to all receive :arrange_clients
102
+ it 'arranges columns in removed client tag' do
103
+ expect(layout.current_tag).to receive :arrange_columns
120
104
  layout.remove client
121
105
  end
122
106
 
@@ -229,8 +213,8 @@ module Uh
229
213
  expect(origin_tag).not_to include client
230
214
  end
231
215
 
232
- it 'shows and hides clients in current column' do
233
- expect(layout.current_column).to receive :show_hide_clients
216
+ it 'removes current client from layout' do
217
+ expect(layout).to receive(:remove).with client
234
218
  layout.handle_tag_set '2'
235
219
  end
236
220
 
@@ -245,10 +229,9 @@ module Uh
245
229
  expect(dest_tag).to include client
246
230
  end
247
231
 
248
- it 'arranges clients in given tag columns' do
249
- layout.current_screen.tags << tag = Layout::Tag.new('2', geo)
250
- expect(tag.current_column_or_create).to receive :arrange_clients
232
+ it 'preserves current tag' do
251
233
  layout.handle_tag_set '2'
234
+ expect(layout.current_tag.id).to eq '1'
252
235
  end
253
236
 
254
237
  it 'updates widgets' do
@@ -290,6 +273,33 @@ module Uh
290
273
  end
291
274
  end
292
275
 
276
+ describe '#handle_column_mode_toggle' do
277
+ context 'without column' do
278
+ it 'does not raise any error' do
279
+ expect { layout.handle_column_mode_toggle }.not_to raise_error
280
+ end
281
+ end
282
+
283
+ context 'with a column' do
284
+ before { layout << client }
285
+
286
+ it 'toggles current column mode' do
287
+ expect(layout.current_column).to receive :mode_toggle
288
+ layout.handle_column_mode_toggle
289
+ end
290
+
291
+ it 'arranges current column clients' do
292
+ expect(layout.current_column).to receive :arrange_clients
293
+ layout.handle_column_mode_toggle
294
+ end
295
+
296
+ it 'shows and hides clients in current column' do
297
+ expect(layout.current_column).to receive :show_hide_clients
298
+ layout.handle_column_mode_toggle
299
+ end
300
+ end
301
+ end
302
+
293
303
  describe '#handle_client_sel' do
294
304
  context 'without client' do
295
305
  it 'does not raise any error' do
@@ -358,22 +368,17 @@ module Uh
358
368
  end
359
369
 
360
370
  context 'with one column and two clients' do
361
- let(:arranger) { instance_spy Layout::Column::Arranger }
371
+ let(:mover) { instance_spy Layout::ClientColumnMover }
362
372
 
363
373
  before { layout << other_client << client }
364
374
 
365
- it 'moves current client with column arranger' do
366
- expect(arranger).to receive(:move_current_client).with(:succ)
367
- layout.handle_client_column_set :succ, arranger: arranger
368
- end
369
-
370
- it 'updates columns geos with column arranger' do
371
- expect(arranger).to receive :update_geos
372
- layout.handle_client_column_set :succ, arranger: arranger
375
+ it 'moves current client with given client column mover' do
376
+ expect(mover).to receive(:move_current).with(:succ)
377
+ layout.handle_client_column_set :succ, mover: mover
373
378
  end
374
379
 
375
- it 'arranges clients in current tag columns' do
376
- expect(layout.current_tag.columns).to all receive :arrange_clients
380
+ it 'arranges current tag columns' do
381
+ expect(layout.current_tag).to receive :arrange_columns
377
382
  layout.handle_client_column_set :succ
378
383
  end
379
384
 
data/uh-layout.gemspec CHANGED
@@ -15,8 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.executables = s.files.grep(/\Abin\//) { |f| File.basename(f) }
16
16
 
17
17
 
18
- s.add_dependency 'uh', '~> 0.1.2'
19
-
18
+ s.add_development_dependency 'uh'
20
19
  s.add_development_dependency 'rake', '~> 10.4'
21
20
  s.add_development_dependency 'rspec', '~> 3.2'
22
21
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uh-layout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thibault Jouan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-04 00:00:00.000000000 Z
11
+ date: 2015-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uh
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.2
20
- type: :runtime
19
+ version: '0'
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.1.2
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,23 +60,28 @@ extra_rdoc_files: []
60
60
  files:
61
61
  - ".gitignore"
62
62
  - ".rspec"
63
+ - ".travis.yml"
63
64
  - Gemfile
64
- - Gemfile-custom.rb
65
65
  - Guardfile
66
66
  - Rakefile
67
67
  - lib/uh/layout.rb
68
+ - lib/uh/layout/arrangers/fixed_width.rb
69
+ - lib/uh/layout/arrangers/stack.rb
70
+ - lib/uh/layout/arrangers/vert_tile.rb
68
71
  - lib/uh/layout/bar.rb
72
+ - lib/uh/layout/client_column_mover.rb
69
73
  - lib/uh/layout/column.rb
70
- - lib/uh/layout/column/arranger.rb
71
74
  - lib/uh/layout/container.rb
72
75
  - lib/uh/layout/dumper.rb
73
- - lib/uh/layout/geo_accessors.rb
74
76
  - lib/uh/layout/screen.rb
75
77
  - lib/uh/layout/tag.rb
76
78
  - lib/uh/layout/version.rb
77
79
  - spec/spec_helper.rb
78
80
  - spec/support/factories.rb
79
- - spec/uh/layout/column/arranger_spec.rb
81
+ - spec/uh/layout/arrangers/fixed_width_spec.rb
82
+ - spec/uh/layout/arrangers/stack_spec.rb
83
+ - spec/uh/layout/arrangers/vert_tile_spec.rb
84
+ - spec/uh/layout/client_column_mover_spec.rb
80
85
  - spec/uh/layout/column_spec.rb
81
86
  - spec/uh/layout/container_spec.rb
82
87
  - spec/uh/layout/screen_spec.rb
@@ -109,7 +114,10 @@ summary: Simple layout for uh
109
114
  test_files:
110
115
  - spec/spec_helper.rb
111
116
  - spec/support/factories.rb
112
- - spec/uh/layout/column/arranger_spec.rb
117
+ - spec/uh/layout/arrangers/fixed_width_spec.rb
118
+ - spec/uh/layout/arrangers/stack_spec.rb
119
+ - spec/uh/layout/arrangers/vert_tile_spec.rb
120
+ - spec/uh/layout/client_column_mover_spec.rb
113
121
  - spec/uh/layout/column_spec.rb
114
122
  - spec/uh/layout/container_spec.rb
115
123
  - spec/uh/layout/screen_spec.rb
data/Gemfile-custom.rb DELETED
@@ -1,7 +0,0 @@
1
- group :development, :test do
2
- gem 'uh', path: "#{ENV['HOME']}/src/my/uh"
3
- gem 'pry'
4
- gem 'listen', require: false, path: "#{ENV['HOME']}/src/sys/listen"
5
- gem 'rb-kqueue', require: false
6
- gem 'guard-rspec', require: false
7
- end
@@ -1,66 +0,0 @@
1
- module Uh
2
- class Layout
3
- class Column
4
- class Arranger
5
- OPTIMAL_WIDTH = 484
6
-
7
- attr_reader :columns, :geo
8
-
9
- def initialize(columns, geo, column_width: OPTIMAL_WIDTH)
10
- @columns = columns
11
- @geo = geo
12
- @column_width = column_width
13
- end
14
-
15
- def redraw
16
- purge
17
- update_geos
18
- yield if block_given?
19
- end
20
-
21
- def purge
22
- @columns.remove_if &:empty?
23
- end
24
-
25
- def move_current_client(direction)
26
- return self unless @columns.current.current_client
27
- @columns.current.remove client = @columns.current.current_client
28
- dest_column = get_or_create_column direction
29
- dest_column << client
30
- dest_column.current_client = client
31
- purge
32
- @columns.current = dest_column
33
- self
34
- end
35
-
36
- def get_or_create_column(direction)
37
- if candidate = @columns.get(direction)
38
- candidate
39
- elsif max_columns_count?
40
- @columns.get direction, cycle: true
41
- else
42
- Column.new(@geo).tap do |o|
43
- case direction
44
- when :pred then @columns.unshift o
45
- when :succ then @columns << o
46
- end
47
- end
48
- end
49
- end
50
-
51
- def max_columns_count?
52
- (@geo.width / (@columns.size + 1)) < @column_width
53
- end
54
-
55
- def update_geos
56
- return if @columns.empty?
57
- @columns.each_with_index do |column, i|
58
- column.x = @column_width * i + @geo.x
59
- column.width = @column_width
60
- end
61
- @columns.last.width = @geo.width - (@columns.last.x - @geo.x)
62
- end
63
- end
64
- end
65
- end
66
- end
@@ -1,10 +0,0 @@
1
- module Uh
2
- class Layout
3
- module GeoAccessors
4
- extend Forwardable
5
- def_delegators :@geo,
6
- :x, :y, :width, :height,
7
- :x=, :y=, :width=, :height=
8
- end
9
- end
10
- end
@@ -1,181 +0,0 @@
1
- module Uh
2
- class Layout
3
- class Column
4
- describe Arranger do
5
- let(:geo) { build_geo }
6
- let(:client) { build_client }
7
- let(:column_width) { 300 }
8
- let(:column) { Column.new(geo) }
9
- let(:columns) { Container.new([column]) }
10
- subject(:arranger) { described_class.new columns, geo,
11
- column_width: column_width }
12
-
13
- describe '#redraw' do
14
- it 'purges columns' do
15
- expect(arranger).to receive :purge
16
- arranger.redraw
17
- end
18
-
19
- it 'updates columns geo' do
20
- expect(arranger).to receive :update_geos
21
- arranger.redraw
22
- end
23
-
24
- it 'yields given block' do
25
- expect { |b| arranger.redraw &b }.to yield_control
26
- end
27
- end
28
-
29
- describe '#purge' do
30
- it 'removes empty columns' do
31
- arranger.purge
32
- expect(columns).to be_empty
33
- end
34
- end
35
-
36
- describe '#move_current_client' do
37
- shared_examples 'moves current client' do |expected_column_index|
38
- it 'removes current client from origin column' do
39
- arranger.move_current_client :succ
40
- expect(column).not_to include client
41
- end
42
-
43
- it 'adds current client in the destination column' do
44
- arranger.move_current_client :succ
45
- expect(columns[expected_column_index]).to include client
46
- end
47
-
48
- it 'updates destination column as the current one' do
49
- arranger.move_current_client :succ
50
- expect(columns.current).to be columns[expected_column_index]
51
- end
52
-
53
- it 'preserves current client as the current one' do
54
- expect { arranger.move_current_client :succ }
55
- .not_to change { columns.current.current_client }
56
- end
57
-
58
- it 'does not leave empty columns' do
59
- expect(columns.none? &:empty?).to be true
60
- end
61
- end
62
-
63
- it 'returns self' do
64
- expect(arranger.move_current_client :succ).to be arranger
65
- end
66
-
67
- context 'given one column with one client' do
68
- before { column << client }
69
-
70
- include_examples 'moves current client', 0
71
- end
72
-
73
- context 'given one column with many clients' do
74
- before { column << client << client.dup }
75
-
76
- include_examples 'moves current client', 1
77
- end
78
-
79
- context 'given two columns' do
80
- let(:columns) { Container.new([column, Column.new(geo)]) }
81
-
82
- before { columns[1] << client.dup }
83
-
84
- context 'when origin column has many clients' do
85
- before { column << client << client.dup }
86
-
87
- include_examples 'moves current client', 1
88
- end
89
-
90
- context 'when origin column has one client' do
91
- before { column << client }
92
-
93
- include_examples 'moves current client', 0
94
- end
95
- end
96
- end
97
-
98
- describe '#get_or_create_column' do
99
- let(:columns) { Container.new([column, Column.new(geo)]) }
100
-
101
- it 'returns the consecutive column in given direction' do
102
- expect(arranger.get_or_create_column :succ).to be columns[1]
103
- end
104
-
105
- context 'when current column is last in given direction' do
106
- before { columns.current = columns[1] }
107
-
108
- context 'when max columns count is not reached' do
109
- before { geo.width = 4096 }
110
-
111
- it 'appends a new column' do
112
- expect(arranger.get_or_create_column :succ).to be columns[2]
113
- end
114
-
115
- it 'prepends a new column' do
116
- columns.current = columns[0]
117
- expect(arranger.get_or_create_column :pred).to be columns[0]
118
- end
119
- end
120
-
121
- context 'when max columns count is reached' do
122
- it 'returns the consecutive column in given direction' do
123
- expect(arranger.get_or_create_column :succ).to be columns[0]
124
- end
125
- end
126
- end
127
- end
128
-
129
- describe '#max_columns_count?' do
130
- context 'when a new column fits in current geo' do
131
- it 'returns false' do
132
- expect(arranger.max_columns_count?).to be false
133
- end
134
- end
135
-
136
- context 'when current geo can not contain more column' do
137
- let(:columns) { Container.new([column, Column.new(geo)]) }
138
-
139
- it 'returns true' do
140
- expect(arranger.max_columns_count?).to be true
141
- end
142
- end
143
- end
144
-
145
- describe '#update_geos' do
146
- let(:columns) { Container.new([column, Column.new(geo)]) }
147
-
148
- before { geo.x = 20 }
149
-
150
- it 'decreases first column width as the optimal column width' do
151
- arranger.update_geos
152
- expect(columns[0].width).to eq 300
153
- end
154
-
155
- it 'offsets each column with given geo' do
156
- arranger.update_geos
157
- expect(columns[0].x).to eq 20
158
- end
159
-
160
- it 'moves second column aside the first column' do
161
- arranger.update_geos
162
- expect(columns[1].x).to eq 320
163
- end
164
-
165
- it 'increases last column width to occupy remaining width' do
166
- arranger.update_geos
167
- expect(columns[1].width).to eq 340
168
- end
169
-
170
- context 'without columns' do
171
- let(:columns) { Container.new([]) }
172
-
173
- it 'does not raise any error' do
174
- expect { arranger.update_geos }.not_to raise_error
175
- end
176
- end
177
- end
178
- end
179
- end
180
- end
181
- end