feat: adding animation for the window border
This commit is contained in:
@@ -21,6 +21,7 @@ $(NAME).zip: dist/extension.js dist/prefs.js schemas/gschemas.compiled
|
|||||||
@cp stylesheet.css dist/
|
@cp stylesheet.css dist/
|
||||||
@(cd dist && zip ../$(NAME).zip -9r .)
|
@(cd dist && zip ../$(NAME).zip -9r .)
|
||||||
|
|
||||||
|
|
||||||
pack: $(NAME).zip
|
pack: $(NAME).zip
|
||||||
|
|
||||||
install: $(NAME).zip
|
install: $(NAME).zip
|
||||||
@@ -34,3 +35,5 @@ clean:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
@dbus-run-session -- gnome-shell --nested --wayland
|
@dbus-run-session -- gnome-shell --nested --wayland
|
||||||
|
|
||||||
|
install-and-test: install test
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
export default class RGBColor {
|
||||||
|
r: number;
|
||||||
|
g: number;
|
||||||
|
b: number;
|
||||||
|
a: number;
|
||||||
|
|
||||||
|
constructor(r: number, g: number, b: number, a: number = 0.8) {
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromHSL(hsl: HSLColor): RGBColor {
|
||||||
|
// Normalize HSL values
|
||||||
|
hsl.h = hsl.h % 360;
|
||||||
|
hsl.s = Math.max(0, Math.min(1, hsl.s));
|
||||||
|
hsl.l = Math.max(0, Math.min(1, hsl.l));
|
||||||
|
hsl.a = Math.max(0, Math.min(1, hsl.a)); // Clamp alpha between 0 and 1
|
||||||
|
|
||||||
|
const c = (1 - Math.abs(2 * hsl.l - 1)) * hsl.s;
|
||||||
|
const x = c * (1 - Math.abs((hsl.h / 60) % 2 - 1));
|
||||||
|
const m = hsl.l - c / 2;
|
||||||
|
let r = 0, g = 0, b = 0;
|
||||||
|
|
||||||
|
if (hsl.h < 60) {
|
||||||
|
r = c;
|
||||||
|
g = x;
|
||||||
|
b = 0;
|
||||||
|
} else if (hsl.h < 120) {
|
||||||
|
r = x;
|
||||||
|
g = c;
|
||||||
|
b = 0;
|
||||||
|
} else if (hsl.h < 180) {
|
||||||
|
r = 0;
|
||||||
|
g = c;
|
||||||
|
b = x;
|
||||||
|
} else if (hsl.h < 240) {
|
||||||
|
r = 0;
|
||||||
|
g = x;
|
||||||
|
b = c;
|
||||||
|
} else if (hsl.h < 300) {
|
||||||
|
r = x;
|
||||||
|
g = 0;
|
||||||
|
b = c;
|
||||||
|
} else {
|
||||||
|
r = c;
|
||||||
|
g = 0;
|
||||||
|
b = x;
|
||||||
|
}
|
||||||
|
return new RGBColor(
|
||||||
|
Math.round((r + m) * 255),
|
||||||
|
Math.round((g + m) * 255),
|
||||||
|
Math.round((b + m) * 255),
|
||||||
|
hsl.a
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HSLColor {
|
||||||
|
h: number;
|
||||||
|
s: number;
|
||||||
|
l: number;
|
||||||
|
a: number;
|
||||||
|
|
||||||
|
constructor(h: number, s: number, l: number, a: number = .8) {
|
||||||
|
this.h = h;
|
||||||
|
this.s = s;
|
||||||
|
this.l = l;
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
+80
-18
@@ -5,6 +5,8 @@ import Meta from 'gi://Meta';
|
|||||||
import Shell from 'gi://Shell';
|
import Shell from 'gi://Shell';
|
||||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||||||
import {Extension, ExtensionMetadata} from 'resource:///org/gnome/shell/extensions/extension.js';
|
import {Extension, ExtensionMetadata} from 'resource:///org/gnome/shell/extensions/extension.js';
|
||||||
|
import Color, {HSLColor} from "./color.js";
|
||||||
|
import RGBColor from "./color.js";
|
||||||
|
|
||||||
export default class ActiveBorderExtension extends Extension {
|
export default class ActiveBorderExtension extends Extension {
|
||||||
|
|
||||||
@@ -13,6 +15,10 @@ export default class ActiveBorderExtension extends Extension {
|
|||||||
lastFocusedWindow: Meta.Window | null;
|
lastFocusedWindow: Meta.Window | null;
|
||||||
_focusSignal: number | null;
|
_focusSignal: number | null;
|
||||||
|
|
||||||
|
// New variables for color.ts cycling
|
||||||
|
colorTimeoutId: number | null;
|
||||||
|
currentColor: HSLColor;
|
||||||
|
|
||||||
constructor(metadata: ExtensionMetadata) {
|
constructor(metadata: ExtensionMetadata) {
|
||||||
super(metadata);
|
super(metadata);
|
||||||
// Initialize instance variables
|
// Initialize instance variables
|
||||||
@@ -20,25 +26,35 @@ export default class ActiveBorderExtension extends Extension {
|
|||||||
this.focusWindowSignals = [];
|
this.focusWindowSignals = [];
|
||||||
this.lastFocusedWindow = null;
|
this.lastFocusedWindow = null;
|
||||||
this._focusSignal = null;
|
this._focusSignal = null;
|
||||||
|
|
||||||
|
// Initialize color.ts cycling variables
|
||||||
|
this.colorTimeoutId = null;
|
||||||
|
this.currentColor = new HSLColor(0, 1, .5); // Starting hue value
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
console.log("STARTING PRETTY BORDERS!")
|
console.log("STARTING PRETTY BORDERS!")
|
||||||
// Connect to the focus window signal to track the active window
|
// Connect to the focus window signal to track the active window
|
||||||
this._focusSignal = global.display.connect('notify::focus-window', () => {
|
this._focusSignal = global.display.connect('notify::focus-window', () => {
|
||||||
|
console.log("Focus Changed")
|
||||||
this._updateBorder(global.display.focus_window);
|
this._updateBorder(global.display.focus_window);
|
||||||
});
|
});
|
||||||
|
// Connect to the "showing" signal for when the overview is opened
|
||||||
|
|
||||||
|
// let grab_begin = global.display.connect("grab-op-begin", () => {
|
||||||
|
// console.log("Grab Started")
|
||||||
|
// })
|
||||||
|
// let grab_end = global.display.connect("grab-op-end", () => {
|
||||||
|
// console.log("Grab Ended")
|
||||||
|
// })
|
||||||
|
|
||||||
// Set initial border on the current window, if there is one
|
// Set initial border on the current window, if there is one
|
||||||
this._updateBorder(global.display.focus_window);
|
this._updateBorder(global.display.focus_window);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateBorder(window: Meta.Window) {
|
_updateBorder(window: Meta.Window) {
|
||||||
console.log("UPDATING THE BORDER")
|
console.log("UPDATING THE BORDER")
|
||||||
// Clear the previous border if there's a last focused window
|
// Clear the previous border
|
||||||
// if (this.lastFocusedWindow) {
|
|
||||||
// this._clearBorder(this.lastFocusedWindow);
|
|
||||||
// }
|
|
||||||
this._clearBorder();
|
this._clearBorder();
|
||||||
// Set a new border for the currently focused window
|
// Set a new border for the currently focused window
|
||||||
if (window) {
|
if (window) {
|
||||||
@@ -57,18 +73,20 @@ export default class ActiveBorderExtension extends Extension {
|
|||||||
// Create a new actor for the border using St.Widget
|
// Create a new actor for the border using St.Widget
|
||||||
this.borderActor = new St.Widget({
|
this.borderActor = new St.Widget({
|
||||||
name: 'active-window-border',
|
name: 'active-window-border',
|
||||||
style_class: 'active-window-border',
|
// style_class: 'active-window-border',
|
||||||
reactive: false,
|
reactive: false,
|
||||||
x: rect.x + 2, // Adjust for border width
|
x: rect.x - 2, // Adjust for border width
|
||||||
y: rect.y - 2,
|
y: rect.y - 2,
|
||||||
width: rect.width + 2,
|
width: rect.width + 4, // Increased to accommodate border
|
||||||
height: rect.height + 2,
|
height: rect.height + 4,
|
||||||
|
// Initial style with default color.ts
|
||||||
|
// style: `border: 4px solid hsl(${this.hue}, 100%, 50%); border-radius: 5px;`,
|
||||||
|
style: `border: 2px solid rgba(0, 0, 0, 0.8); border-radius: 3px;`
|
||||||
});
|
});
|
||||||
// this.borderActor = new St.BorderImage({
|
|
||||||
//
|
|
||||||
// })
|
|
||||||
// Add the border actor to the UI group
|
// Add the border actor to the UI group
|
||||||
Main.layoutManager.uiGroup.add_child(this.borderActor);
|
global.window_group.add_child(this.borderActor);
|
||||||
|
// Main.layoutManager.uiGroup.add_child(this.borderActor);
|
||||||
|
|
||||||
// Listen to window's changes in position and size
|
// Listen to window's changes in position and size
|
||||||
this.focusWindowSignals?.push(window.connect('position-changed', () => this._updateBorderPosition(window)));
|
this.focusWindowSignals?.push(window.connect('position-changed', () => this._updateBorderPosition(window)));
|
||||||
@@ -76,8 +94,9 @@ export default class ActiveBorderExtension extends Extension {
|
|||||||
this.focusWindowSignals?.push(window.connect('unmanaged', () => this._clearBorder()));
|
this.focusWindowSignals?.push(window.connect('unmanaged', () => this._clearBorder()));
|
||||||
|
|
||||||
this._updateBorderPosition(window);
|
this._updateBorderPosition(window);
|
||||||
// Add the effect to the window
|
|
||||||
// actor.add_effect_with_name('bor/der-effect', borderEffect);
|
// Start the color.ts cycling
|
||||||
|
this._startColorCycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateBorderPosition(window: Meta.Window) {
|
_updateBorderPosition(window: Meta.Window) {
|
||||||
@@ -86,11 +105,14 @@ export default class ActiveBorderExtension extends Extension {
|
|||||||
const rect = window.get_frame_rect();
|
const rect = window.get_frame_rect();
|
||||||
if (!rect) return;
|
if (!rect) return;
|
||||||
|
|
||||||
this.borderActor.set_position(rect.x + 2, rect.y - 2);
|
this.borderActor.set_position(rect.x - 2, rect.y - 2);
|
||||||
this.borderActor.set_size(rect.width - 2, rect.height + 2);
|
this.borderActor.set_size(rect.width + 4, rect.height + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearBorder() {
|
_clearBorder() {
|
||||||
|
// Stop the color.ts cycling
|
||||||
|
this._stopColorCycle();
|
||||||
|
|
||||||
if (this.borderActor) {
|
if (this.borderActor) {
|
||||||
this.borderActor.destroy();
|
this.borderActor.destroy();
|
||||||
this.borderActor = null;
|
this.borderActor = null;
|
||||||
@@ -107,7 +129,7 @@ export default class ActiveBorderExtension extends Extension {
|
|||||||
|
|
||||||
disable() {
|
disable() {
|
||||||
console.log("DISABLED PRETTY BORDERS!")
|
console.log("DISABLED PRETTY BORDERS!")
|
||||||
// Disconnect the signal and remove any existing borders
|
// Disconnect the focus signal and remove any existing borders
|
||||||
if (this._focusSignal) {
|
if (this._focusSignal) {
|
||||||
global.display.disconnect(this._focusSignal);
|
global.display.disconnect(this._focusSignal);
|
||||||
this._focusSignal = null;
|
this._focusSignal = null;
|
||||||
@@ -118,4 +140,44 @@ export default class ActiveBorderExtension extends Extension {
|
|||||||
this.lastFocusedWindow = null;
|
this.lastFocusedWindow = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the color.ts cycling using GLib.timeout_add
|
||||||
|
_startColorCycle() {
|
||||||
|
if (this.colorTimeoutId === null) {
|
||||||
|
// Update every 100 milliseconds
|
||||||
|
this.colorTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 33, () => {
|
||||||
|
this._updateColor();
|
||||||
|
// Continue the timeout
|
||||||
|
return GLib.SOURCE_CONTINUE;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_getStyleRGBA() {
|
||||||
|
let rgb = RGBColor.fromHSL(this.currentColor)
|
||||||
|
return `border: 3px solid rgba(${rgb.r}, ${rgb.b}, ${rgb.g}, ${rgb.a}); border-radius: 10px;`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the color.ts cycling
|
||||||
|
_stopColorCycle() {
|
||||||
|
if (this.colorTimeoutId !== null) {
|
||||||
|
GLib.source_remove(this.colorTimeoutId);
|
||||||
|
this.colorTimeoutId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the border color.ts based on the current hue
|
||||||
|
_updateColor() {
|
||||||
|
if (!this.borderActor) return;
|
||||||
|
// console.log("updating color.ts")
|
||||||
|
|
||||||
|
// console.log(this.borderActor.get_style());
|
||||||
|
// Increment hue and wrap around at 360
|
||||||
|
this.currentColor.h = (this.currentColor.h + 1) % 360;
|
||||||
|
|
||||||
|
// Update the border color.ts
|
||||||
|
this.borderActor.set_style(this._getStyleRGBA());
|
||||||
|
return true; // Continue the timeout
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+15
-15
@@ -1,14 +1,14 @@
|
|||||||
/* Add your custom extension styling here */
|
/* Add your custom extension styling here */
|
||||||
.active-window-border {
|
.active-window-border {
|
||||||
border: 2px solid rgba(191, 0, 255, 0.8);
|
/*border: 2px solid rgba(191, 0, 255, 0.8);*/
|
||||||
border-radius: 3px;
|
/*border-radius: 3px;*/
|
||||||
|
|
||||||
border-image-source: linear-gradient(to left, #743ad5, #d53a9d);
|
/* border-image-source: linear-gradient(to left, #743ad5, #d53a9d);*/
|
||||||
/*border: 4px solid transparent;*/
|
/* !*border: 4px solid transparent;*!*/
|
||||||
/*border-radius: 5px;*/
|
/* !*border-radius: 5px;*!*/
|
||||||
|
|
||||||
/*/* Gradient border using border-image */
|
/* !*!* Gradient border using border-image *!*/
|
||||||
border-image: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1;
|
/* border-image: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) 1;*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,14 +16,14 @@
|
|||||||
/* border-image-source: linear-gradient(to left, #743ad5, #d53a9d);*/
|
/* border-image-source: linear-gradient(to left, #743ad5, #d53a9d);*/
|
||||||
/*}*/
|
/*}*/
|
||||||
|
|
||||||
@keyframes rainbow-border {
|
/*@keyframes rainbow-border {*/
|
||||||
0% {
|
/* 0% {*/
|
||||||
border-image: linear-gradient(0deg, red, orange, yellow, green, blue, indigo, violet, red) 1;
|
/* border-image: linear-gradient(0deg, red, orange, yellow, green, blue, indigo, violet, red) 1;*/
|
||||||
}
|
/* }*/
|
||||||
100% {
|
/* 100% {*/
|
||||||
border-image: linear-gradient(360deg, red, orange, yellow, green, blue, indigo, violet, red) 1;
|
/* border-image: linear-gradient(360deg, red, orange, yellow, green, blue, indigo, violet, red) 1;*/
|
||||||
}
|
/* }*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
/*.active-window-border {*/
|
/*.active-window-border {*/
|
||||||
/* border: 4px solid transparent;*/
|
/* border: 4px solid transparent;*/
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"extension.ts",
|
"extension.ts",
|
||||||
|
"color.ts"
|
||||||
// "prefs.ts"
|
// "prefs.ts"
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user