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