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:
Zander Brown 2024-06-28 12:39:24 +00:00
commit 5cba22d46a
6 changed files with 697 additions and 130 deletions

View file

@ -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'),

View file

@ -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
View 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
View 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

View 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)
{
}

View 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