uh-layout 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +5 -0
- data/Gemfile-custom.rb +7 -0
- data/Guardfile +6 -0
- data/Rakefile +5 -0
- data/lib/uh/layout.rb +161 -0
- data/lib/uh/layout/bar.rb +159 -0
- data/lib/uh/layout/column.rb +44 -0
- data/lib/uh/layout/column/arranger.rb +66 -0
- data/lib/uh/layout/container.rb +75 -0
- data/lib/uh/layout/dumper.rb +28 -0
- data/lib/uh/layout/geo_accessors.rb +10 -0
- data/lib/uh/layout/screen.rb +32 -0
- data/lib/uh/layout/tag.rb +47 -0
- data/lib/uh/layout/version.rb +5 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/factories.rb +9 -0
- data/spec/uh/layout/column/arranger_spec.rb +181 -0
- data/spec/uh/layout/column_spec.rb +66 -0
- data/spec/uh/layout/container_spec.rb +170 -0
- data/spec/uh/layout/screen_spec.rb +40 -0
- data/spec/uh/layout/tag_spec.rb +62 -0
- data/spec/uh/layout_spec.rb +414 -0
- data/uh-layout.gemspec +22 -0
- metadata +118 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ccb6aa0368780ba45a6f29d289f76cf3518fa3af
|
4
|
+
data.tar.gz: bed1e1fea17ff3c4f774484fa75c082edb420d06
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7a094e49e315b7f364af1bb4d1468efbf7086e2c54b883440a68a609eb65f24e7df1b462e3e5d60b9cfa4cf9776ce673eb42a471622bfc15a2f9a65028481da5
|
7
|
+
data.tar.gz: fbd5e5522acd4f377f1e8610f95120c0912dbe7d80aa70e0e230833de4a4769fda49fdd48c482354c5a291c72fa13a43f815c7b8e400aa9a06eedcbf4f4d9a0e
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/Gemfile
ADDED
data/Gemfile-custom.rb
ADDED
data/Guardfile
ADDED
data/Rakefile
ADDED
data/lib/uh/layout.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
require 'uh/layout/geo_accessors'
|
4
|
+
require 'uh/layout/bar'
|
5
|
+
require 'uh/layout/container'
|
6
|
+
require 'uh/layout/column'
|
7
|
+
require 'uh/layout/column/arranger'
|
8
|
+
require 'uh/layout/dumper'
|
9
|
+
require 'uh/layout/screen'
|
10
|
+
require 'uh/layout/tag'
|
11
|
+
|
12
|
+
module Uh
|
13
|
+
class Layout
|
14
|
+
extend Forwardable
|
15
|
+
def_delegator :@screens, :current, :current_screen
|
16
|
+
def_delegator :current_screen, :==, :current_screen?
|
17
|
+
def_delegator :current_screen, :current_tag
|
18
|
+
def_delegator :current_tag, :current_column
|
19
|
+
|
20
|
+
attr_reader :screens, :widgets
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@screens = Container.new
|
24
|
+
@widgets = []
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
Dumper.new(self).to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
def current_client
|
32
|
+
current_column and current_column.current_client
|
33
|
+
end
|
34
|
+
|
35
|
+
def include?(client)
|
36
|
+
screens.any? { |screen| screen.include? client }
|
37
|
+
end
|
38
|
+
|
39
|
+
def arranger_for_current_tag
|
40
|
+
Column::Arranger.new(current_tag.columns, current_tag.geo)
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_widgets
|
44
|
+
@widgets.each &:update
|
45
|
+
@widgets.each &:redraw
|
46
|
+
end
|
47
|
+
|
48
|
+
def suggest_geo
|
49
|
+
(current_column or current_tag).geo.dup
|
50
|
+
end
|
51
|
+
|
52
|
+
def <<(client)
|
53
|
+
current_tag.current_column_or_create << client
|
54
|
+
current_column.current_client = client
|
55
|
+
current_column.arrange_clients
|
56
|
+
current_column.show_hide_clients
|
57
|
+
client.focus
|
58
|
+
update_widgets
|
59
|
+
self
|
60
|
+
end
|
61
|
+
alias push <<
|
62
|
+
|
63
|
+
def remove(client)
|
64
|
+
screen, tag, column = find_client client
|
65
|
+
column.remove client
|
66
|
+
Column::Arranger.new(tag.columns, tag.geo).redraw
|
67
|
+
tag.each_column &:arrange_clients
|
68
|
+
column.show_hide_clients
|
69
|
+
current_client.focus if current_client
|
70
|
+
update_widgets
|
71
|
+
end
|
72
|
+
|
73
|
+
def handle_screen_sel(direction)
|
74
|
+
screens.sel direction
|
75
|
+
current_client.focus if current_client
|
76
|
+
update_widgets
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_screen_set(direction)
|
80
|
+
return unless current_client
|
81
|
+
remove client = current_client
|
82
|
+
screens.sel direction
|
83
|
+
push client
|
84
|
+
end
|
85
|
+
|
86
|
+
def handle_tag_sel(tag_id)
|
87
|
+
return unless current_tag.id != tag_id
|
88
|
+
current_tag.hide
|
89
|
+
current_screen.tags.current = find_tag_or_create tag_id
|
90
|
+
current_tag.each_column &:show_hide_clients
|
91
|
+
current_client.focus if current_client
|
92
|
+
update_widgets
|
93
|
+
end
|
94
|
+
|
95
|
+
def handle_tag_set(tag_id)
|
96
|
+
return unless current_client && current_tag.id != tag_id
|
97
|
+
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
|
105
|
+
end
|
106
|
+
|
107
|
+
def handle_column_sel(direction)
|
108
|
+
return unless current_tag.columns.any?
|
109
|
+
current_tag.columns.sel direction
|
110
|
+
current_client.focus
|
111
|
+
update_widgets
|
112
|
+
end
|
113
|
+
|
114
|
+
def handle_client_sel(direction)
|
115
|
+
return unless current_client
|
116
|
+
current_column.clients.sel direction
|
117
|
+
current_column.show_hide_clients
|
118
|
+
current_client.focus
|
119
|
+
update_widgets
|
120
|
+
end
|
121
|
+
|
122
|
+
def handle_client_swap(direction)
|
123
|
+
return unless current_client
|
124
|
+
current_column.clients.set direction
|
125
|
+
update_widgets
|
126
|
+
end
|
127
|
+
|
128
|
+
def handle_client_column_set(direction, arranger: arranger_for_current_tag)
|
129
|
+
return unless current_client
|
130
|
+
arranger.move_current_client(direction).update_geos
|
131
|
+
current_tag.each_column &:arrange_clients
|
132
|
+
current_tag.each_column &:show_hide_clients
|
133
|
+
update_widgets
|
134
|
+
end
|
135
|
+
|
136
|
+
def handle_kill_current
|
137
|
+
current_client and current_client.kill
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def find_client(client)
|
144
|
+
screens.each do |screen|
|
145
|
+
screen.tags.each do |tag|
|
146
|
+
tag.each_column do |column|
|
147
|
+
if column.include? client
|
148
|
+
return screen, tag, column
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def find_tag_or_create(tag_id)
|
156
|
+
current_screen.tags.find { |e| e.id == tag_id } or Tag.new(tag_id, current_screen.geo).tap do |tag|
|
157
|
+
current_screen.tags << tag
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module Uh
|
2
|
+
class Layout
|
3
|
+
class Bar
|
4
|
+
TEXT_PADDING_X = 1
|
5
|
+
TEXT_PADDING_Y = 1
|
6
|
+
COLUMN_MARGIN_TOP = 0
|
7
|
+
COLUMN_HEIGHT = 2
|
8
|
+
COLUMN_PADDING_X = 1
|
9
|
+
TAG_PADDING_X = 5
|
10
|
+
|
11
|
+
include GeoAccessors
|
12
|
+
|
13
|
+
attr_writer :active
|
14
|
+
|
15
|
+
def initialize(display, screen, colors)
|
16
|
+
@display = display
|
17
|
+
@screen = screen
|
18
|
+
@geo = build_geo @screen.geo
|
19
|
+
@window = @display.create_subwindow @geo
|
20
|
+
@pixmap = @display.create_pixmap width, height
|
21
|
+
@colors = Hash[colors.map { |k, v| [k, @display.color_by_name(v)] }]
|
22
|
+
@on_update = proc { }
|
23
|
+
end
|
24
|
+
|
25
|
+
def active?
|
26
|
+
!!@active
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_update(&block)
|
30
|
+
@on_update = block
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
@on_update.call
|
35
|
+
end
|
36
|
+
|
37
|
+
def redraw
|
38
|
+
draw_background
|
39
|
+
draw_columns @screen.current_tag.columns,
|
40
|
+
@screen.current_tag.current_column
|
41
|
+
draw_tags @screen.tags, @screen.current_tag
|
42
|
+
blit
|
43
|
+
end
|
44
|
+
|
45
|
+
def show
|
46
|
+
@window.show
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def focus
|
51
|
+
@window.focus
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def blit
|
59
|
+
@pixmap.copy @window
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_geo(layout_geo)
|
64
|
+
bar_height = text_line_height * 2 + COLUMN_HEIGHT + 1
|
65
|
+
|
66
|
+
Uh::Geo.new(
|
67
|
+
layout_geo.x,
|
68
|
+
layout_geo.height - bar_height,
|
69
|
+
layout_geo.width,
|
70
|
+
bar_height
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def active_color
|
75
|
+
active? ? @colors[:sel] : @colors[:hi]
|
76
|
+
end
|
77
|
+
|
78
|
+
def text_line_height
|
79
|
+
@display.font.height + TEXT_PADDING_Y * 2
|
80
|
+
end
|
81
|
+
|
82
|
+
def column_widget_text_y
|
83
|
+
COLUMN_MARGIN_TOP + COLUMN_HEIGHT
|
84
|
+
end
|
85
|
+
|
86
|
+
def column_widget_height
|
87
|
+
column_widget_text_y + text_line_height + 1
|
88
|
+
end
|
89
|
+
|
90
|
+
def column_offset_x(column)
|
91
|
+
column.x - x
|
92
|
+
end
|
93
|
+
|
94
|
+
def column_text(column)
|
95
|
+
text = '%d/%d %s (%s)' % [
|
96
|
+
column.clients.index(column.current_client),
|
97
|
+
column.clients.size,
|
98
|
+
column.current_client.name,
|
99
|
+
column.current_client.wclass
|
100
|
+
]
|
101
|
+
end
|
102
|
+
|
103
|
+
def draw_background
|
104
|
+
@pixmap.gc_color @colors[:bg]
|
105
|
+
@pixmap.draw_rect 0, 0, width, height
|
106
|
+
end
|
107
|
+
|
108
|
+
def draw_columns(columns, current_column)
|
109
|
+
columns.each do |column|
|
110
|
+
draw_column column, column == current_column
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def draw_column(column, current)
|
115
|
+
@pixmap.gc_color current ? active_color : @colors[:hi]
|
116
|
+
@pixmap.draw_rect column_offset_x(column) + COLUMN_PADDING_X,
|
117
|
+
COLUMN_MARGIN_TOP,
|
118
|
+
column.width - COLUMN_PADDING_X, COLUMN_HEIGHT
|
119
|
+
@pixmap.gc_color @colors[:fg]
|
120
|
+
text_y =
|
121
|
+
column_widget_text_y + @display.font.ascent + TEXT_PADDING_Y
|
122
|
+
@pixmap.draw_string column_offset_x(column) + TEXT_PADDING_Y,
|
123
|
+
text_y, column_text(column)
|
124
|
+
end
|
125
|
+
|
126
|
+
def draw_tags(tags, current_tag)
|
127
|
+
tags.sort_by(&:id).inject(0) do |offset, tag|
|
128
|
+
color = if tag == current_tag
|
129
|
+
active_color
|
130
|
+
elsif tag.clients.any?
|
131
|
+
@colors[:hi]
|
132
|
+
else
|
133
|
+
@colors[:bg]
|
134
|
+
end
|
135
|
+
|
136
|
+
offset + draw_text(
|
137
|
+
tag.id, offset, column_widget_height,
|
138
|
+
@colors[:fg], color,
|
139
|
+
TAG_PADDING_X
|
140
|
+
)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def draw_text(text, x, y, color_fg = @colors[:fg], color_bg = nil,
|
145
|
+
padding_x = TEXT_PADDING_X)
|
146
|
+
text = text.to_s
|
147
|
+
text_width = text.length * @display.font.width + padding_x * 2
|
148
|
+
text_y = y + @display.font.ascent + TEXT_PADDING_Y
|
149
|
+
if color_bg
|
150
|
+
@pixmap.gc_color color_bg
|
151
|
+
@pixmap.draw_rect x, y, text_width, text_line_height
|
152
|
+
end
|
153
|
+
@pixmap.gc_color color_fg
|
154
|
+
@pixmap.draw_string x + padding_x, text_y, text
|
155
|
+
text_width
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Uh
|
2
|
+
class Layout
|
3
|
+
class Column
|
4
|
+
include GeoAccessors
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@clients, :empty?, :include?, :remove
|
8
|
+
def_delegator :@clients, :current, :current_client
|
9
|
+
def_delegator :@clients, :current=, :current_client=
|
10
|
+
def_delegator :current_client, :==, :current_client?
|
11
|
+
|
12
|
+
attr_reader :geo, :clients
|
13
|
+
|
14
|
+
def initialize(geo)
|
15
|
+
@geo = geo.dup
|
16
|
+
@clients = Container.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"COL geo: #{@geo}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def <<(client)
|
24
|
+
client.geo = @geo.dup
|
25
|
+
@clients << client
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def arrange_clients
|
30
|
+
@clients.each do |client|
|
31
|
+
client.geo = @geo.dup
|
32
|
+
client.moveresize
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
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?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,66 @@
|
|
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
|