Skip to content

Commit 556d5f5

Browse files
authored
Fix workspace-grid for Cinnamon 5.4 (#4405)
1 parent ce540d2 commit 556d5f5

File tree

10 files changed

+601
-0
lines changed

10 files changed

+601
-0
lines changed
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
const Lang = imports.lang;
2+
const St = imports.gi.St;
3+
const Clutter = imports.gi.Clutter;
4+
5+
function BarIndicatorStyle(applet, cols, rows, height) {
6+
this._init(applet, cols, rows, height);
7+
}
8+
9+
BarIndicatorStyle.prototype = {
10+
11+
_init: function(applet, cols, rows, height) {
12+
this.applet = applet;
13+
this.button = [];
14+
this.update_grid(cols, rows, height);
15+
this.switch_id = global.window_manager.connect('switch-workspace', Lang.bind(this, this.update));
16+
this.scroll_id = this.applet.actor.connect('scroll-event', Lang.bind(this,this.onMouseScroll));
17+
},
18+
19+
update_grid: function(cols, rows, height) {
20+
this.cols = cols;
21+
this.rows = rows;
22+
this.height = height;
23+
this.rebuild();
24+
},
25+
26+
cleanup: function() {
27+
global.window_manager.disconnect(this.switch_id);
28+
this.applet.actor.disconnect(this.scroll_id);
29+
},
30+
31+
onMouseScroll: function(actor, event){
32+
if (this.scrollby == 'row')
33+
this.scrollByRow(event);
34+
else
35+
this.scrollByCol(event);
36+
},
37+
38+
scrollByCol: function(event) {
39+
var idx = global.screen.get_active_workspace_index();
40+
41+
if (event.get_scroll_direction() == 0) idx--;
42+
else if (event.get_scroll_direction() == 1) idx++;
43+
44+
if(global.screen.get_workspace_by_index(idx) != null)
45+
global.screen.get_workspace_by_index(idx).activate(global.get_current_time());
46+
},
47+
48+
scrollByRow: function(event) {
49+
var idx = global.screen.get_active_workspace_index();
50+
var numworkspaces = this.rows * this.cols;
51+
52+
var row = Math.floor(idx/this.cols);
53+
var col = idx % this.cols;
54+
55+
if (event.get_scroll_direction() == 0) {
56+
row--;
57+
if (row < 0) {
58+
row=this.rows-1;
59+
col--;
60+
}
61+
}
62+
else if (event.get_scroll_direction() == 1) {
63+
row++;
64+
if (row >= this.rows) {
65+
row=0;
66+
col++;
67+
}
68+
}
69+
70+
if (col < 0 || col >= this.cols)
71+
return;
72+
73+
idx = row*this.cols + col;
74+
75+
if(global.screen.get_workspace_by_index(idx) != null)
76+
global.screen.get_workspace_by_index(idx).activate(global.get_current_time());
77+
},
78+
79+
onRowIndicatorClicked: function(actor, event) {
80+
if (event.get_button() != 1) return false;
81+
82+
let curws_idx = global.screen.get_active_workspace_index();
83+
let curws_row = Math.floor(curws_idx/this.cols);
84+
let [x, y] = event.get_coords();
85+
let [wx, wy] = actor.get_transformed_position();
86+
let [w, h] = actor.get_size();
87+
y -= wy;
88+
89+
let clicked_row = Math.floor(this.rows*y/h);
90+
clicked_idx = (clicked_row * this.cols) + (curws_idx % this.cols);
91+
92+
global.screen.get_workspace_by_index(clicked_idx).activate(global.get_current_time());
93+
return true;
94+
},
95+
96+
onWorkspaceButtonClicked: function(actor, event) {
97+
if (event.get_button() != 1) return false;
98+
global.screen.get_workspace_by_index(actor.index).activate(global.get_current_time());
99+
},
100+
101+
setReactivity: function(reactive) {
102+
for (let i=0; i < this.button.length; ++i)
103+
this.button[i].set_reactive(reactive);
104+
},
105+
106+
rebuild: function() {
107+
this.applet.actor.destroy_all_children();
108+
109+
if (this.rows > 1) {
110+
this.row_indicator = new St.DrawingArea({ reactive: true });
111+
this.row_indicator.set_width(this.height/1.75);
112+
this.row_indicator.connect('repaint', Lang.bind(this, this.draw_row_indicator));
113+
this.row_indicator.connect('button-press-event', Lang.bind(this, this.onRowIndicatorClicked));
114+
this.applet.actor.add(this.row_indicator);
115+
}
116+
117+
this.button = [];
118+
for ( let i=0; i<global.screen.n_workspaces; ++i ) {
119+
this.button[i] = new St.Button({ name: 'workspaceButton', style_class: 'workspace-button', reactive: true });
120+
121+
let text = (i+1).toString();
122+
let label = new St.Label({ text: text });
123+
label.set_style("font-weight: bold");
124+
this.button[i].set_child(label);
125+
this.applet.actor.add(this.button[i]);
126+
this.button[i].index = i;
127+
this.button[i].set_height(this.height);
128+
this.button[i].set_width(this.height*1.25);
129+
this.button[i].connect('button-release-event', Lang.bind(this, this.onWorkspaceButtonClicked));
130+
}
131+
this.update();
132+
},
133+
134+
update: function() {
135+
let nworks = this.button.length;
136+
let active_ws = global.screen.get_active_workspace_index();
137+
let active_row = Math.floor(active_ws/this.cols);
138+
let low = (active_row)*this.cols;
139+
let high = low + this.cols;
140+
141+
// If the user added or removed workspaces external to this applet then
142+
// we could end up with a selected workspaces that is out of bounds. Just
143+
// revert to displaying the last row in that case.
144+
if (active_ws >= nworks) {
145+
high = nworks - 1;
146+
low = high - this.cols;
147+
}
148+
149+
for (let i=0; i < nworks; ++i) {
150+
if (i >= low && i < high) this.button[i].show();
151+
else this.button[i].hide();
152+
153+
if (i == active_ws) {
154+
this.button[i].get_child().set_text((i+1).toString());
155+
this.button[i].add_style_pseudo_class('outlined');
156+
}
157+
else {
158+
this.button[i].get_child().set_text((i+1).toString());
159+
this.button[i].remove_style_pseudo_class('outlined');
160+
}
161+
}
162+
163+
if ( this.row_indicator ) {
164+
this.row_indicator.queue_repaint();
165+
}
166+
},
167+
168+
draw_row_indicator: function(area) {
169+
let [width, height] = area.get_surface_size();
170+
let themeNode = this.row_indicator.get_theme_node();
171+
let cr = area.get_context();
172+
173+
let base_color = this.get_base_color();
174+
let active_color = null;
175+
let inactive_color = null;
176+
177+
if (this.is_theme_light_on_dark()) {
178+
active_color = base_color.lighten();
179+
inactive_color = base_color.darken();
180+
}
181+
else {
182+
active_color = base_color.darken().darken();
183+
inactive_color = base_color.lighten().lighten();
184+
}
185+
186+
let active = global.screen.get_active_workspace_index();
187+
let active_row = Math.floor(active/this.cols);
188+
189+
// Catch overflow due to externally added/removed workspaces
190+
if (active >= this.button.length) active_row = (this.button.length-1) /this.cols;
191+
192+
for ( let i=0; i < this.rows; ++i ) {
193+
let y = (i+1)*height/(this.rows+1);
194+
let endx = (width / 10) * 9;
195+
cr.moveTo(0, y);
196+
cr.lineTo(endx, y);
197+
let color = active_row == i ? active_color : inactive_color;
198+
Clutter.cairo_set_source_color(cr, color);
199+
cr.setLineWidth(2.0);
200+
cr.stroke();
201+
}
202+
},
203+
204+
is_theme_light_on_dark: function() {
205+
let selected_idx = global.screen.get_active_workspace_index();
206+
let unselected_idx = 0;
207+
if (unselected_idx == selected_idx) unselected_idx = 1;
208+
209+
let selected_txt_color = this.button[selected_idx].get_theme_node().get_color('color');
210+
let unselected_txt_color = this.button[unselected_idx].get_theme_node().get_color('color');
211+
212+
let sel_avg = (selected_txt_color.red + selected_txt_color.green + selected_txt_color.blue)/3;
213+
let unsel_avg = (unselected_txt_color.red + unselected_txt_color.green + unselected_txt_color.blue)/3;
214+
return (sel_avg < unsel_avg);
215+
},
216+
217+
// All colors we use in this applet are based on this theme defined color.
218+
// We simply grab the color of a normal, non-outlined workspae button.
219+
get_base_color: function() {
220+
let unselected_idx = 0;
221+
if (unselected_idx == global.screen.get_active_workspace_index()) unselected_idx = 1;
222+
return this.button[unselected_idx].get_theme_node().get_color('color');
223+
}
224+
};
225+
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
const Lang = imports.lang;
2+
const St = imports.gi.St;
3+
const Clutter = imports.gi.Clutter;
4+
5+
function GridStyle(applet, cols, rows, height) {
6+
this._init(applet, cols, rows, height);
7+
}
8+
9+
GridStyle.prototype = {
10+
11+
_init: function(applet, cols, rows, height) {
12+
this.scrollby = 'col';
13+
this.applet = applet;
14+
this.button = [];
15+
this.update_grid(cols, rows, height);
16+
this.event_handlers = [];
17+
this.switch_id = global.window_manager.connect('switch-workspace', Lang.bind(this, this.update));
18+
this.scroll_id = this.applet.actor.connect('scroll-event', Lang.bind(this,this.onMouseScroll));
19+
},
20+
21+
cleanup: function() {
22+
global.window_manager.disconnect(this.switch_id);
23+
this.applet.actor.disconnect(this.scroll_id);
24+
},
25+
26+
update_grid: function(cols, rows, height) {
27+
this.cols = cols;
28+
this.rows = rows;
29+
this.height = height;
30+
this.rebuild();
31+
},
32+
33+
onMouseScroll: function(actor, event){
34+
if (this.scrollby == 'row')
35+
this.scrollByRow(event);
36+
else
37+
this.scrollByCol(event);
38+
},
39+
40+
scrollByCol: function(event) {
41+
var idx = global.screen.get_active_workspace_index();
42+
43+
if (event.get_scroll_direction() == 0) idx--;
44+
else if (event.get_scroll_direction() == 1) idx++;
45+
46+
if(global.screen.get_workspace_by_index(idx) != null)
47+
global.screen.get_workspace_by_index(idx).activate(global.get_current_time());
48+
},
49+
50+
scrollByRow: function(event) {
51+
var idx = global.screen.get_active_workspace_index();
52+
var numworkspaces = this.rows * this.cols;
53+
54+
var row = Math.floor(idx/this.cols);
55+
var col = idx % this.cols ;
56+
57+
if (event.get_scroll_direction() == 0) {
58+
row--;
59+
if (row < 0) {
60+
row=this.rows-1;
61+
col--;
62+
}
63+
}
64+
else if (event.get_scroll_direction() == 1) {
65+
row++;
66+
if (row >= this.rows) {
67+
row=0;
68+
col++;
69+
}
70+
}
71+
72+
if (col < 0 || col >= this.cols)
73+
return;
74+
75+
idx = row*this.cols + col;
76+
77+
if(global.screen.get_workspace_by_index(idx) != null)
78+
global.screen.get_workspace_by_index(idx).activate(global.get_current_time());
79+
},
80+
81+
onWorkspaceButtonClicked: function(actor, event) {
82+
if (event.get_button() != 1) return false;
83+
global.screen.get_workspace_by_index(actor.index).activate(global.get_current_time());
84+
},
85+
86+
setReactivity: function(reactive) {
87+
for (let i=0; i < this.button.length; ++i)
88+
this.button[i].reactive = reactive;
89+
},
90+
91+
rebuild: function() {
92+
this.applet.actor.destroy_all_children();
93+
this.table = new St.Table({homogeneous: false, reactive: true });
94+
this.applet.actor.add(this.table);
95+
96+
let btn_height = this.height/this.rows;
97+
this.button = [];
98+
for(let r=0; r < this.rows; r++) {
99+
for(let c=0; c < this.cols; c++) {
100+
let i = (r*this.cols)+c;
101+
102+
this.button[i] = new St.Button({ name: 'workspaceButton', style_class: 'workspace-button', reactive: true });
103+
this.button[i].index = i;
104+
this.button[i].set_height(btn_height);
105+
this.button[i].set_width(btn_height*1.25);
106+
this.button[i].connect('button-release-event', Lang.bind(this, this.onWorkspaceButtonClicked));
107+
this.table.add(this.button[i], {row: r, col: c});
108+
}
109+
}
110+
this.update();
111+
},
112+
113+
update: function() {
114+
let active_ws = global.screen.get_active_workspace_index();
115+
116+
for (let i=0; i < this.button.length; ++i) {
117+
if (i == active_ws)
118+
this.button[i].add_style_pseudo_class('outlined');
119+
else
120+
this.button[i].remove_style_pseudo_class('outlined');
121+
}
122+
}
123+
};
124+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const TOPLEFT = imports.gi.Meta.DisplayCorner.TOPLEFT;
2+
3+
function WorkspaceController(cols, rows) {
4+
this._init(cols, rows);
5+
}
6+
7+
WorkspaceController.prototype = {
8+
9+
_init: function(cols, rows) {
10+
this.set_workspace_grid(cols, rows);
11+
},
12+
13+
// Create proper workspace layout geometry within Gnome
14+
set_workspace_grid: function (cols, rows) {
15+
this.cols = cols;
16+
this.rows = rows;
17+
this.__equalize_num_workspaces();
18+
global.screen.override_workspace_layout(TOPLEFT, false, rows, cols);
19+
},
20+
21+
// Update Gnome's view of workspaces to reflect our count based on row*col.
22+
__equalize_num_workspaces: function() {
23+
let new_ws_count = this.cols * this.rows;
24+
let old_ws_count = global.screen.n_workspaces;
25+
26+
if (new_ws_count > old_ws_count) {
27+
for (let i=old_ws_count; i<new_ws_count; i++)
28+
global.screen.append_new_workspace(false, global.get_current_time());
29+
}
30+
else if (new_ws_count < old_ws_count) {
31+
for (let i=old_ws_count; i>new_ws_count; i--) {
32+
let ws = global.screen.get_workspace_by_index( global.screen.n_workspaces-1 );
33+
global.screen.remove_workspace(ws, global.get_current_time());
34+
}
35+
}
36+
},
37+
38+
// This applet is going away. Revert to allowing Cinnamon to control workspaces.
39+
release_control: function() {
40+
this.set_workspace_grid(-1, 1); // Set to no rows, and a single desktop
41+
}
42+
};
43+

0 commit comments

Comments
 (0)