mirror of
https://github.com/edu4rdshl/gnome-shell.git
synced 2026-07-17 23:24:51 +00:00
Merge branch 'zbrown/c-dots' into 'main'
Move Workspace{Dot,Indicators} from JS to C
See merge request GNOME/gnome-shell!2992
This commit is contained in:
commit
5cba22d46a
6 changed files with 697 additions and 130 deletions
140
js/ui/panel.js
140
js/ui/panel.js
|
|
@ -4,7 +4,6 @@ import Atk from 'gi://Atk';
|
||||||
import Clutter from 'gi://Clutter';
|
import Clutter from 'gi://Clutter';
|
||||||
import GLib from 'gi://GLib';
|
import GLib from 'gi://GLib';
|
||||||
import GObject from 'gi://GObject';
|
import GObject from 'gi://GObject';
|
||||||
import Graphene from 'gi://Graphene';
|
|
||||||
import Meta from 'gi://Meta';
|
import Meta from 'gi://Meta';
|
||||||
import Shell from 'gi://Shell';
|
import Shell from 'gi://Shell';
|
||||||
import St from 'gi://St';
|
import St from 'gi://St';
|
||||||
|
|
@ -19,7 +18,6 @@ import * as PopupMenu from './popupMenu.js';
|
||||||
import * as PanelMenu from './panelMenu.js';
|
import * as PanelMenu from './panelMenu.js';
|
||||||
import {QuickSettingsMenu, SystemIndicator} from './quickSettings.js';
|
import {QuickSettingsMenu, SystemIndicator} from './quickSettings.js';
|
||||||
import * as Main from './main.js';
|
import * as Main from './main.js';
|
||||||
import * as Util from '../misc/util.js';
|
|
||||||
|
|
||||||
import * as RemoteAccessStatus from './status/remoteAccess.js';
|
import * as RemoteAccessStatus from './status/remoteAccess.js';
|
||||||
import * as PowerProfileStatus from './status/powerProfiles.js';
|
import * as PowerProfileStatus from './status/powerProfiles.js';
|
||||||
|
|
@ -49,7 +47,6 @@ const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
|
||||||
|
|
||||||
const N_QUICK_SETTINGS_COLUMNS = 2;
|
const N_QUICK_SETTINGS_COLUMNS = 2;
|
||||||
|
|
||||||
const INACTIVE_WORKSPACE_DOT_SCALE = 0.75;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AppMenuButton:
|
* AppMenuButton:
|
||||||
|
|
@ -260,68 +257,9 @@ const AppMenuButton = GObject.registerClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const WorkspaceDot = GObject.registerClass({
|
const WorkspaceDot = GObject.registerClass(
|
||||||
Properties: {
|
class WorkspaceDot extends Shell.WorkspaceDot {
|
||||||
'expansion': GObject.ParamSpec.double('expansion', '', '',
|
vfunc_scale_in() {
|
||||||
GObject.ParamFlags.READWRITE,
|
|
||||||
0.0, 1.0, 0.0),
|
|
||||||
'width-multiplier': GObject.ParamSpec.double(
|
|
||||||
'width-multiplier', '', '',
|
|
||||||
GObject.ParamFlags.READWRITE,
|
|
||||||
1.0, 10.0, 1.0),
|
|
||||||
},
|
|
||||||
}, class WorkspaceDot extends Clutter.Actor {
|
|
||||||
constructor(params = {}) {
|
|
||||||
super({
|
|
||||||
pivot_point: new Graphene.Point({x: 0.5, y: 0.5}),
|
|
||||||
...params,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._dot = new St.Widget({
|
|
||||||
style_class: 'workspace-dot',
|
|
||||||
y_align: Clutter.ActorAlign.CENTER,
|
|
||||||
pivot_point: new Graphene.Point({x: 0.5, y: 0.5}),
|
|
||||||
request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT,
|
|
||||||
});
|
|
||||||
this.add_child(this._dot);
|
|
||||||
|
|
||||||
this.connect('notify::width-multiplier', () => this.queue_relayout());
|
|
||||||
this.connect('notify::expansion', () => {
|
|
||||||
this._updateVisuals();
|
|
||||||
this.queue_relayout();
|
|
||||||
});
|
|
||||||
this._updateVisuals();
|
|
||||||
|
|
||||||
this._destroying = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateVisuals() {
|
|
||||||
const {expansion} = this;
|
|
||||||
|
|
||||||
this._dot.set({
|
|
||||||
opacity: Util.lerp(0.50, 1.0, expansion) * 255,
|
|
||||||
scaleX: Util.lerp(INACTIVE_WORKSPACE_DOT_SCALE, 1.0, expansion),
|
|
||||||
scaleY: Util.lerp(INACTIVE_WORKSPACE_DOT_SCALE, 1.0, expansion),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_get_preferred_width(forHeight) {
|
|
||||||
const factor = Util.lerp(1.0, this.widthMultiplier, this.expansion);
|
|
||||||
return this._dot.get_preferred_width(forHeight).map(v => Math.round(v * factor));
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_get_preferred_height(forWidth) {
|
|
||||||
return this._dot.get_preferred_height(forWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
vfunc_allocate(box) {
|
|
||||||
this.set_allocation(box);
|
|
||||||
|
|
||||||
box.set_origin(0, 0);
|
|
||||||
this._dot.allocate(box);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleIn() {
|
|
||||||
this.set({
|
this.set({
|
||||||
scale_x: 0,
|
scale_x: 0,
|
||||||
scale_y: 0,
|
scale_y: 0,
|
||||||
|
|
@ -335,9 +273,7 @@ const WorkspaceDot = GObject.registerClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
scaleOutAndDestroy() {
|
vfunc_scale_out_and_destroy() {
|
||||||
this._destroying = true;
|
|
||||||
|
|
||||||
this.ease({
|
this.ease({
|
||||||
duration: 500,
|
duration: 500,
|
||||||
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
||||||
|
|
@ -346,68 +282,14 @@ const WorkspaceDot = GObject.registerClass({
|
||||||
onComplete: () => this.destroy(),
|
onComplete: () => this.destroy(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get destroying() {
|
|
||||||
return this._destroying;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const WorkspaceIndicators = GObject.registerClass(
|
const WorkspaceIndicators = GObject.registerClass(
|
||||||
class WorkspaceIndicators extends St.BoxLayout {
|
class WorkspaceIndicators extends Shell.WorkspaceIndicators {
|
||||||
constructor() {
|
constructor(params = {}) {
|
||||||
super();
|
super({
|
||||||
|
dot_type: WorkspaceDot.$gtype,
|
||||||
this._workspacesAdjustment = Main.createWorkspacesAdjustment(this);
|
...params,
|
||||||
this._workspacesAdjustment.connectObject(
|
|
||||||
'notify::value', () => this._updateExpansion(),
|
|
||||||
'notify::upper', () => this._recalculateDots(),
|
|
||||||
this);
|
|
||||||
|
|
||||||
for (let i = 0; i < this._workspacesAdjustment.upper; i++)
|
|
||||||
this.insert_child_at_index(new WorkspaceDot(), i);
|
|
||||||
this._updateExpansion();
|
|
||||||
}
|
|
||||||
|
|
||||||
_getActiveIndicators() {
|
|
||||||
return [...this].filter(i => !i.destroying);
|
|
||||||
}
|
|
||||||
|
|
||||||
_recalculateDots() {
|
|
||||||
const activeIndicators = this._getActiveIndicators();
|
|
||||||
const nIndicators = activeIndicators.length;
|
|
||||||
const targetIndicators = this._workspacesAdjustment.upper;
|
|
||||||
|
|
||||||
let remaining = Math.abs(nIndicators - targetIndicators);
|
|
||||||
while (remaining--) {
|
|
||||||
if (nIndicators < targetIndicators) {
|
|
||||||
const indicator = new WorkspaceDot();
|
|
||||||
this.add_child(indicator);
|
|
||||||
indicator.scaleIn();
|
|
||||||
} else {
|
|
||||||
const indicator = activeIndicators[nIndicators - remaining - 1];
|
|
||||||
indicator.scaleOutAndDestroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updateExpansion();
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateExpansion() {
|
|
||||||
const nIndicators = this._getActiveIndicators().length;
|
|
||||||
const activeWorkspace = this._workspacesAdjustment.value;
|
|
||||||
|
|
||||||
let widthMultiplier;
|
|
||||||
if (nIndicators <= 2)
|
|
||||||
widthMultiplier = 3.625;
|
|
||||||
else if (nIndicators <= 5)
|
|
||||||
widthMultiplier = 3.25;
|
|
||||||
else
|
|
||||||
widthMultiplier = 2.75;
|
|
||||||
|
|
||||||
this.get_children().forEach((indicator, index) => {
|
|
||||||
const distance = Math.abs(index - activeWorkspace);
|
|
||||||
indicator.expansion = Math.clamp(1 - distance, 0, 1);
|
|
||||||
indicator.widthMultiplier = widthMultiplier;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -425,7 +307,9 @@ class ActivitiesButton extends PanelMenu.Button {
|
||||||
accessible_name: _('Activities'),
|
accessible_name: _('Activities'),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.add_child(new WorkspaceIndicators());
|
this.add_child(new WorkspaceIndicators({
|
||||||
|
workspaces_adjustment: Main.createWorkspacesAdjustment(this),
|
||||||
|
}));
|
||||||
|
|
||||||
Main.overview.connectObject('showing',
|
Main.overview.connectObject('showing',
|
||||||
() => this.add_style_pseudo_class('checked'),
|
() => this.add_style_pseudo_class('checked'),
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,9 @@ libshell_public_headers = [
|
||||||
'shell-window-preview-layout.h',
|
'shell-window-preview-layout.h',
|
||||||
'shell-window-tracker.h',
|
'shell-window-tracker.h',
|
||||||
'shell-wm.h',
|
'shell-wm.h',
|
||||||
'shell-workspace-background.h'
|
'shell-workspace-background.h',
|
||||||
|
'shell-workspace-dot.h',
|
||||||
|
'shell-workspace-indicators.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
if have_x11_client
|
if have_x11_client
|
||||||
|
|
@ -186,7 +188,9 @@ libshell_sources = [
|
||||||
'shell-window-preview-layout.c',
|
'shell-window-preview-layout.c',
|
||||||
'shell-window-tracker.c',
|
'shell-window-tracker.c',
|
||||||
'shell-wm.c',
|
'shell-wm.c',
|
||||||
'shell-workspace-background.c'
|
'shell-workspace-background.c',
|
||||||
|
'shell-workspace-dot.c',
|
||||||
|
'shell-workspace-indicators.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
if have_x11_client
|
if have_x11_client
|
||||||
|
|
|
||||||
345
src/shell-workspace-dot.c
Normal file
345
src/shell-workspace-dot.c
Normal file
|
|
@ -0,0 +1,345 @@
|
||||||
|
/* shell-workspace-dot.c
|
||||||
|
*
|
||||||
|
* Copyright 2023 Zander Brown <zbrown@gnome.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <graphene.h>
|
||||||
|
#include <st/st.h>
|
||||||
|
|
||||||
|
#include "shell-workspace-dot.h"
|
||||||
|
|
||||||
|
#define INACTIVE_WORKSPACE_DOT_SCALE 0.75
|
||||||
|
|
||||||
|
typedef struct _ShellWorkspaceDotPrivate ShellWorkspaceDotPrivate;
|
||||||
|
struct _ShellWorkspaceDotPrivate
|
||||||
|
{
|
||||||
|
ClutterActor *dot;
|
||||||
|
|
||||||
|
float width_multiplier;
|
||||||
|
float expansion;
|
||||||
|
|
||||||
|
gboolean destroying;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
|
||||||
|
PROP_WIDTH_MULTIPLIER,
|
||||||
|
PROP_EXPANSION,
|
||||||
|
PROP_DESTROYING,
|
||||||
|
|
||||||
|
PROP_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ShellWorkspaceDot, shell_workspace_dot, CLUTTER_TYPE_ACTOR)
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (SHELL_WORKSPACE_DOT (object));
|
||||||
|
|
||||||
|
g_clear_object (&priv->dot);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (shell_workspace_dot_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float
|
||||||
|
lerp (float start, float end, float progress)
|
||||||
|
{
|
||||||
|
return start + progress * (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_visuals (ShellWorkspaceDot *self)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (self);
|
||||||
|
float scale = lerp (INACTIVE_WORKSPACE_DOT_SCALE, 1.0, priv->expansion);
|
||||||
|
|
||||||
|
clutter_actor_set_opacity (priv->dot,
|
||||||
|
lerp (0.50, 1.0, priv->expansion) * 255);
|
||||||
|
clutter_actor_set_scale (priv->dot, scale, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_set_property (GObject *object,
|
||||||
|
unsigned int property_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDot *self = SHELL_WORKSPACE_DOT (object);
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (self);
|
||||||
|
|
||||||
|
switch (property_id)
|
||||||
|
{
|
||||||
|
case PROP_WIDTH_MULTIPLIER:
|
||||||
|
shell_workspace_dot_set_state (self,
|
||||||
|
priv->expansion,
|
||||||
|
g_value_get_double (value));
|
||||||
|
break;
|
||||||
|
case PROP_EXPANSION:
|
||||||
|
shell_workspace_dot_set_state (self,
|
||||||
|
g_value_get_double (value),
|
||||||
|
priv->width_multiplier);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_get_property (GObject *object,
|
||||||
|
unsigned int property_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (SHELL_WORKSPACE_DOT (object));
|
||||||
|
|
||||||
|
switch (property_id)
|
||||||
|
{
|
||||||
|
case PROP_WIDTH_MULTIPLIER:
|
||||||
|
g_value_set_double (value, priv->width_multiplier);
|
||||||
|
break;
|
||||||
|
case PROP_EXPANSION:
|
||||||
|
g_value_set_double (value, priv->expansion);
|
||||||
|
break;
|
||||||
|
case PROP_DESTROYING:
|
||||||
|
g_value_set_boolean (value, priv->destroying);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_get_preferred_width (ClutterActor *actor,
|
||||||
|
float for_height,
|
||||||
|
float *min_width_p,
|
||||||
|
float *natural_width_p)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (SHELL_WORKSPACE_DOT (actor));
|
||||||
|
float factor = lerp (1.0, priv->width_multiplier, priv->expansion);
|
||||||
|
float min, nat;
|
||||||
|
|
||||||
|
clutter_actor_get_preferred_width (priv->dot, for_height, &min, &nat);
|
||||||
|
|
||||||
|
if (min_width_p)
|
||||||
|
*min_width_p = min * factor;
|
||||||
|
|
||||||
|
if (natural_width_p)
|
||||||
|
*natural_width_p = nat * factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_get_preferred_height (ClutterActor *actor,
|
||||||
|
float for_width,
|
||||||
|
float *min_height_p,
|
||||||
|
float *natural_height_p)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (SHELL_WORKSPACE_DOT (actor));
|
||||||
|
|
||||||
|
clutter_actor_get_preferred_height (priv->dot,
|
||||||
|
for_width,
|
||||||
|
min_height_p,
|
||||||
|
natural_height_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_allocate (ClutterActor *actor,
|
||||||
|
const ClutterActorBox *box)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (SHELL_WORKSPACE_DOT (actor));
|
||||||
|
ClutterActorBox dot_box = *box;
|
||||||
|
|
||||||
|
clutter_actor_set_allocation (actor, box);
|
||||||
|
|
||||||
|
clutter_actor_box_set_origin (&dot_box, 0, 0);
|
||||||
|
clutter_actor_allocate (priv->dot, &dot_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_class_init (ShellWorkspaceDotClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = shell_workspace_dot_dispose;
|
||||||
|
object_class->set_property = shell_workspace_dot_set_property;
|
||||||
|
object_class->get_property = shell_workspace_dot_get_property;
|
||||||
|
|
||||||
|
actor_class->get_preferred_width = shell_workspace_dot_get_preferred_width;
|
||||||
|
actor_class->get_preferred_height = shell_workspace_dot_get_preferred_height;
|
||||||
|
actor_class->allocate = shell_workspace_dot_allocate;
|
||||||
|
|
||||||
|
obj_props[PROP_WIDTH_MULTIPLIER] =
|
||||||
|
g_param_spec_double ("width-multiplier", NULL, NULL,
|
||||||
|
1.0, 10.0, 1.0,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
obj_props[PROP_EXPANSION] =
|
||||||
|
g_param_spec_double ("expansion", NULL, NULL,
|
||||||
|
0.0, 1.0, 0.0,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ShellWorkspaceDot:destroying: (attributes org.gtk.Property.get=shell_workspace_dot_is_destroying)
|
||||||
|
*/
|
||||||
|
obj_props[PROP_DESTROYING] =
|
||||||
|
g_param_spec_boolean ("destroying", NULL, NULL,
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_dot_init (ShellWorkspaceDot *self)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv =
|
||||||
|
shell_workspace_dot_get_instance_private (self);
|
||||||
|
|
||||||
|
clutter_actor_set_pivot_point (CLUTTER_ACTOR (self), 0.5, 0.5);
|
||||||
|
|
||||||
|
priv->dot = g_object_new (ST_TYPE_WIDGET,
|
||||||
|
"style-class", "workspace-dot",
|
||||||
|
"y-align", CLUTTER_ACTOR_ALIGN_CENTER,
|
||||||
|
"pivot-point", &GRAPHENE_POINT_INIT (0.5, 0.5),
|
||||||
|
"request-mode", CLUTTER_REQUEST_WIDTH_FOR_HEIGHT,
|
||||||
|
NULL);
|
||||||
|
g_object_ref_sink (priv->dot);
|
||||||
|
clutter_actor_add_child (CLUTTER_ACTOR (self), priv->dot);
|
||||||
|
|
||||||
|
priv->destroying = FALSE;
|
||||||
|
|
||||||
|
update_visuals (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline gboolean
|
||||||
|
value_changed (float old_value, float new_value)
|
||||||
|
{
|
||||||
|
return !G_APPROX_VALUE (old_value, new_value, FLT_EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
set_if_changed (GObject *object,
|
||||||
|
GParamSpec *pspec,
|
||||||
|
float *target,
|
||||||
|
float new_value)
|
||||||
|
{
|
||||||
|
if (G_LIKELY (value_changed (*target, new_value)))
|
||||||
|
{
|
||||||
|
*target = new_value;
|
||||||
|
g_object_notify_by_pspec (object, pspec);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
shell_workspace_dot_set_state (ShellWorkspaceDot *self,
|
||||||
|
float expansion,
|
||||||
|
float width_multiplier)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv;
|
||||||
|
GObject *object = G_OBJECT (self);
|
||||||
|
gboolean trigger_layout = FALSE;
|
||||||
|
|
||||||
|
g_return_if_fail (SHELL_IS_WORKSPACE_DOT (self));
|
||||||
|
|
||||||
|
priv = shell_workspace_dot_get_instance_private (self);
|
||||||
|
|
||||||
|
g_object_freeze_notify (object);
|
||||||
|
|
||||||
|
trigger_layout = set_if_changed (object,
|
||||||
|
obj_props[PROP_WIDTH_MULTIPLIER],
|
||||||
|
&priv->width_multiplier,
|
||||||
|
width_multiplier);
|
||||||
|
|
||||||
|
if (set_if_changed (object,
|
||||||
|
obj_props[PROP_EXPANSION],
|
||||||
|
&priv->expansion,
|
||||||
|
expansion))
|
||||||
|
{
|
||||||
|
update_visuals (self);
|
||||||
|
trigger_layout = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_thaw_notify (object);
|
||||||
|
|
||||||
|
if (G_LIKELY (trigger_layout))
|
||||||
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
shell_workspace_dot_is_destroying (ShellWorkspaceDot *self)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv;
|
||||||
|
|
||||||
|
g_return_val_if_fail (SHELL_IS_WORKSPACE_DOT (self), FALSE);
|
||||||
|
|
||||||
|
priv = shell_workspace_dot_get_instance_private (self);
|
||||||
|
|
||||||
|
return priv->destroying;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
shell_workspace_dot_scale_in (ShellWorkspaceDot *self)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotClass *klass;
|
||||||
|
|
||||||
|
g_return_if_fail (SHELL_IS_WORKSPACE_DOT (self));
|
||||||
|
|
||||||
|
klass = SHELL_WORKSPACE_DOT_GET_CLASS (self);
|
||||||
|
|
||||||
|
if (G_LIKELY (klass->scale_in))
|
||||||
|
klass->scale_in (self);
|
||||||
|
else
|
||||||
|
g_warning ("%s didn't override scale_in", G_OBJECT_TYPE_NAME (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
shell_workspace_dot_scale_out_and_destroy (ShellWorkspaceDot *self)
|
||||||
|
{
|
||||||
|
ShellWorkspaceDotPrivate *priv;
|
||||||
|
ShellWorkspaceDotClass *klass;
|
||||||
|
|
||||||
|
g_return_if_fail (SHELL_IS_WORKSPACE_DOT (self));
|
||||||
|
|
||||||
|
priv = shell_workspace_dot_get_instance_private (self);
|
||||||
|
priv->destroying = TRUE;
|
||||||
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DESTROYING]);
|
||||||
|
|
||||||
|
klass = SHELL_WORKSPACE_DOT_GET_CLASS (self);
|
||||||
|
|
||||||
|
if (G_LIKELY (klass->scale_out_and_destroy))
|
||||||
|
klass->scale_out_and_destroy (self);
|
||||||
|
else
|
||||||
|
g_warning ("%s didn't override scale_out_and_destroy",
|
||||||
|
G_OBJECT_TYPE_NAME (self));
|
||||||
|
}
|
||||||
45
src/shell-workspace-dot.h
Normal file
45
src/shell-workspace-dot.h
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* shell-workspace-dot.h
|
||||||
|
*
|
||||||
|
* Copyright 2023 Zander Brown <zbrown@gnome.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define SHELL_TYPE_WORKSPACE_DOT (shell_workspace_dot_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (ShellWorkspaceDot, shell_workspace_dot, SHELL, WORKSPACE_DOT, ClutterActor)
|
||||||
|
|
||||||
|
struct _ShellWorkspaceDotClass
|
||||||
|
{
|
||||||
|
ClutterActorClass parent_class;
|
||||||
|
|
||||||
|
void (*scale_in) (ShellWorkspaceDot *dot);
|
||||||
|
void (*scale_out_and_destroy) (ShellWorkspaceDot *dot);
|
||||||
|
};
|
||||||
|
|
||||||
|
void shell_workspace_dot_set_state (ShellWorkspaceDot *self,
|
||||||
|
float expansion,
|
||||||
|
float width_multiplier);
|
||||||
|
gboolean shell_workspace_dot_is_destroying (ShellWorkspaceDot *self);
|
||||||
|
void shell_workspace_dot_scale_in (ShellWorkspaceDot *self);
|
||||||
|
void shell_workspace_dot_scale_out_and_destroy (ShellWorkspaceDot *self);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
252
src/shell-workspace-indicators.c
Normal file
252
src/shell-workspace-indicators.c
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
/* shell-workspace-dot.c
|
||||||
|
*
|
||||||
|
* Copyright 2023 Zander Brown <zbrown@gnome.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include "shell-workspace-dot.h"
|
||||||
|
#include "shell-workspace-indicators.h"
|
||||||
|
|
||||||
|
typedef struct _ShellWorkspaceIndicatorsPrivate ShellWorkspaceIndicatorsPrivate;
|
||||||
|
struct _ShellWorkspaceIndicatorsPrivate
|
||||||
|
{
|
||||||
|
GType dot_type;
|
||||||
|
StAdjustment *workspaces_adjustment;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
|
||||||
|
PROP_DOT_TYPE,
|
||||||
|
PROP_WORKSPACES_ADJUSTMENT,
|
||||||
|
|
||||||
|
PROP_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (ShellWorkspaceIndicators, shell_workspace_indicators, ST_TYPE_BOX_LAYOUT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_indicators_dispose (GObject *object)
|
||||||
|
{
|
||||||
|
ShellWorkspaceIndicatorsPrivate *priv =
|
||||||
|
shell_workspace_indicators_get_instance_private (SHELL_WORKSPACE_INDICATORS (object));
|
||||||
|
|
||||||
|
g_clear_object (&priv->workspaces_adjustment);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (shell_workspace_indicators_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GPtrArray *
|
||||||
|
get_active_dots (ShellWorkspaceIndicators *self)
|
||||||
|
{
|
||||||
|
GPtrArray *active = g_ptr_array_new ();
|
||||||
|
ClutterActor *child;
|
||||||
|
|
||||||
|
for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
|
||||||
|
child != NULL;
|
||||||
|
child = clutter_actor_get_next_sibling (child))
|
||||||
|
{
|
||||||
|
if (!shell_workspace_dot_is_destroying (SHELL_WORKSPACE_DOT (child)))
|
||||||
|
g_ptr_array_add (active, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_expansion (ShellWorkspaceIndicators *self)
|
||||||
|
{
|
||||||
|
ShellWorkspaceIndicatorsPrivate *priv;
|
||||||
|
g_autoptr (GPtrArray) active_dots = NULL;
|
||||||
|
size_t n_indicators;
|
||||||
|
float active_workspace, width_multiplier;
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
g_return_if_fail (SHELL_IS_WORKSPACE_INDICATORS (self));
|
||||||
|
|
||||||
|
priv = shell_workspace_indicators_get_instance_private (self);
|
||||||
|
|
||||||
|
active_dots = get_active_dots (self);
|
||||||
|
n_indicators = active_dots->len;
|
||||||
|
active_workspace = st_adjustment_get_value (priv->workspaces_adjustment);
|
||||||
|
|
||||||
|
if (n_indicators <= 2)
|
||||||
|
width_multiplier = 3.625;
|
||||||
|
else if (n_indicators <= 5)
|
||||||
|
width_multiplier = 3.25;
|
||||||
|
else
|
||||||
|
width_multiplier = 2.75;
|
||||||
|
|
||||||
|
for (ClutterActor *child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
|
||||||
|
child != NULL;
|
||||||
|
index++, child = clutter_actor_get_next_sibling (child))
|
||||||
|
{
|
||||||
|
float distance = fabsf (((float) index) - active_workspace);
|
||||||
|
|
||||||
|
shell_workspace_dot_set_state (SHELL_WORKSPACE_DOT (child),
|
||||||
|
CLAMP (1.0 - distance, 0.0, 1.0),
|
||||||
|
width_multiplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
recalculate_dots (ShellWorkspaceIndicators *self)
|
||||||
|
{
|
||||||
|
ShellWorkspaceIndicatorsPrivate *priv =
|
||||||
|
shell_workspace_indicators_get_instance_private (self);
|
||||||
|
ShellWorkspaceDot *dot;
|
||||||
|
g_autoptr (GPtrArray) active_dots = get_active_dots (self);
|
||||||
|
size_t remaining;
|
||||||
|
double target;
|
||||||
|
|
||||||
|
st_adjustment_get_values (priv->workspaces_adjustment,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&target,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
remaining = abs (active_dots->len - target);
|
||||||
|
|
||||||
|
while (remaining--)
|
||||||
|
if (active_dots->len < target)
|
||||||
|
{
|
||||||
|
dot = g_object_new (priv->dot_type, NULL);
|
||||||
|
clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (dot));
|
||||||
|
shell_workspace_dot_scale_in (dot);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dot = g_ptr_array_index (active_dots,
|
||||||
|
active_dots->len - remaining - 1);
|
||||||
|
shell_workspace_dot_scale_out_and_destroy (dot);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_expansion (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_indicators_set_property (GObject *object,
|
||||||
|
unsigned int property_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
ShellWorkspaceIndicators *self = SHELL_WORKSPACE_INDICATORS (object);
|
||||||
|
ShellWorkspaceIndicatorsPrivate *priv =
|
||||||
|
shell_workspace_indicators_get_instance_private (self);
|
||||||
|
ShellWorkspaceDot *dot;
|
||||||
|
double upper;
|
||||||
|
|
||||||
|
switch (property_id)
|
||||||
|
{
|
||||||
|
case PROP_DOT_TYPE:
|
||||||
|
priv->dot_type = g_value_get_gtype (value);
|
||||||
|
g_object_notify_by_pspec (object, pspec);
|
||||||
|
break;
|
||||||
|
case PROP_WORKSPACES_ADJUSTMENT:
|
||||||
|
priv->workspaces_adjustment = g_value_dup_object (value);
|
||||||
|
g_signal_connect_object (priv->workspaces_adjustment,
|
||||||
|
"notify::value",
|
||||||
|
G_CALLBACK (update_expansion),
|
||||||
|
object,
|
||||||
|
G_CONNECT_SWAPPED);
|
||||||
|
g_signal_connect_object (priv->workspaces_adjustment,
|
||||||
|
"notify::upper",
|
||||||
|
G_CALLBACK (recalculate_dots),
|
||||||
|
object,
|
||||||
|
G_CONNECT_SWAPPED);
|
||||||
|
|
||||||
|
st_adjustment_get_values (priv->workspaces_adjustment,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&upper,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ((size_t) upper); i++)
|
||||||
|
{
|
||||||
|
dot = g_object_new (priv->dot_type, NULL);
|
||||||
|
clutter_actor_insert_child_at_index (CLUTTER_ACTOR (self),
|
||||||
|
CLUTTER_ACTOR (dot),
|
||||||
|
i);
|
||||||
|
}
|
||||||
|
update_expansion (self);
|
||||||
|
|
||||||
|
g_object_notify_by_pspec (object, pspec);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_indicators_get_property (GObject *object,
|
||||||
|
unsigned int property_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
ShellWorkspaceIndicators *self = SHELL_WORKSPACE_INDICATORS (object);
|
||||||
|
ShellWorkspaceIndicatorsPrivate *priv =
|
||||||
|
shell_workspace_indicators_get_instance_private (self);
|
||||||
|
|
||||||
|
switch (property_id)
|
||||||
|
{
|
||||||
|
case PROP_DOT_TYPE:
|
||||||
|
g_value_set_gtype (value, priv->dot_type);
|
||||||
|
break;
|
||||||
|
case PROP_WORKSPACES_ADJUSTMENT:
|
||||||
|
g_value_set_object (value, priv->workspaces_adjustment);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_indicators_class_init (ShellWorkspaceIndicatorsClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->dispose = shell_workspace_indicators_dispose;
|
||||||
|
object_class->set_property = shell_workspace_indicators_set_property;
|
||||||
|
object_class->get_property = shell_workspace_indicators_get_property;
|
||||||
|
|
||||||
|
obj_props[PROP_DOT_TYPE] =
|
||||||
|
g_param_spec_gtype ("dot-type", NULL, NULL,
|
||||||
|
SHELL_TYPE_WORKSPACE_DOT,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
obj_props[PROP_WORKSPACES_ADJUSTMENT] =
|
||||||
|
g_param_spec_object ("workspaces-adjustment", NULL, NULL,
|
||||||
|
ST_TYPE_ADJUSTMENT,
|
||||||
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||||
|
|
||||||
|
g_object_class_install_properties (object_class, PROP_LAST, obj_props);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
shell_workspace_indicators_init (ShellWorkspaceIndicators *self)
|
||||||
|
{
|
||||||
|
}
|
||||||
37
src/shell-workspace-indicators.h
Normal file
37
src/shell-workspace-indicators.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/* shell-workspace-indicators.h
|
||||||
|
*
|
||||||
|
* Copyright 2023 Zander Brown <zbrown@gnome.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <st/st.h>
|
||||||
|
|
||||||
|
#include "shell-workspace-dot.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define SHELL_TYPE_WORKSPACE_INDICATORS (shell_workspace_indicators_get_type ())
|
||||||
|
G_DECLARE_DERIVABLE_TYPE (ShellWorkspaceIndicators, shell_workspace_indicators, SHELL, WORKSPACE_INDICATORS, StBoxLayout)
|
||||||
|
|
||||||
|
struct _ShellWorkspaceIndicatorsClass
|
||||||
|
{
|
||||||
|
StBoxLayoutClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
Loading…
Add table
Add a link
Reference in a new issue