diff --git a/extension.js b/extension.js index dee6049..93458e6 100644 --- a/extension.js +++ b/extension.js @@ -38,6 +38,7 @@ const ALL_EFFECTS = [ Me.imports.src.EnergizeA.EnergizeA, Me.imports.src.EnergizeB.EnergizeB, Me.imports.src.Fire.Fire, + Me.imports.src.Hexagon.Hexagon, Me.imports.src.Matrix.Matrix, Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, Me.imports.src.TRexAttack.TRexAttack, diff --git a/prefs.js b/prefs.js index c225aba..04e7f54 100644 --- a/prefs.js +++ b/prefs.js @@ -37,6 +37,7 @@ const ALL_EFFECTS = [ Me.imports.src.EnergizeA.EnergizeA, Me.imports.src.EnergizeB.EnergizeB, Me.imports.src.Fire.Fire, + Me.imports.src.Hexagon.Hexagon, Me.imports.src.Matrix.Matrix, Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, Me.imports.src.TRexAttack.TRexAttack, diff --git a/resources/ui/gtk3/Hexagon.ui b/resources/ui/gtk3/Hexagon.ui new file mode 100644 index 0000000..8f1f9ee --- /dev/null +++ b/resources/ui/gtk3/Hexagon.ui @@ -0,0 +1,340 @@ + + + + + 10000 + 100 + 1 + 100 + + + + 10 + 0.1 + 0.01 + 0.1 + + + + 10 + 0 + 0.1 + 1 + + + + vertical + + + + + + none + + + + + 10 + 10 + 10 + 10 + 0 + + + 8 + + + Animation Time [ms] + 0 + start + center + 1 + + + + + end + center + 1 + 0 + left + 300 + hexagon-animation-time + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + 10 + 10 + 10 + 10 + 0 + + + 8 + + + Scale + 0 + start + center + 1 + + + + + end + center + 1 + 2 + left + 300 + hexagon-scale + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + 10 + 10 + 10 + 10 + 0 + + + 8 + + + Mesh Line Width + 0 + start + center + 1 + + + + + end + center + 1 + 1 + left + 300 + hexagon-line-width + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + 10 + 10 + 10 + 10 + 0 + + + + + Line Color + 0 + start + center + 1 + + + + + 1 + 1 + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + 10 + 10 + 10 + 10 + 0 + + + + + Glow Color + 0 + start + center + 1 + + + + + 1 + 1 + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + 10 + 10 + 10 + 10 + 0 + + + + + vertical + 3 + + + Additive Blending + 0 + start + center + 1 + + + + + You may want to enable this for dark window themes. + 0 + start + center + 1 + + + + + + + + end + center + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/ui/gtk4/Hexagon.ui b/resources/ui/gtk4/Hexagon.ui new file mode 100644 index 0000000..798f125 --- /dev/null +++ b/resources/ui/gtk4/Hexagon.ui @@ -0,0 +1,284 @@ + + + + + vertical + + + + + + none + 1 + + + + + 0 + + + + + Animation Time [ms] + 0 + start + center + 1 + + + + + end + center + 1 + 0 + left + 300 + + + 10000 + 100 + 1 + 100 + + + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + 0 + + + + + Scale + 0 + start + center + 1 + + + + + end + center + 1 + 2 + left + 300 + + + 10 + 0.1 + 0.01 + 0.1 + + + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + 0 + + + + + Mesh Line Width + 0 + start + center + 1 + + + + + end + center + 1 + 1 + left + 300 + + + 10 + 0 + 0.1 + 1 + + + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + 0 + + + + + Line Color + 0 + start + center + 1 + + + + + 1 + 1 + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + 0 + + + + + Glow Color + 0 + start + center + 1 + + + + + 1 + 1 + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + 0 + + + + + vertical + 3 + + + Additive Blending + 0 + start + center + 1 + + + + + You may want to enable this for dark window themes. + 0 + start + center + 1 + + + + + + + + end + center + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml index 99f1bee..f852dad 100644 --- a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml +++ b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml @@ -273,6 +273,58 @@ Number of claw scratches drawn on top of each other. + + + + + + false + Hexagon Close Effect + Use the Hexagon effect for window closing. + + + + false + Hexagon Open Effect + Use the Hexagon effect for window opening. + + + + 1500 + Hexagon Animation Time + The time the Hexagon effect takes. + + + + 3.0 + Hexagon Scale + The size of the effect. + + + + 2.0 + Hexagon Line Width + The width of the lines. + + + + "rgba(200, 255, 255, 0.5)" + Hexagon Line Color + The color of the lines. + + + + "rgba(20, 80, 255, 0.5)" + Hexagon Glow Color + The color of the glow effect. + + + + true + Hexagon Additive Blending + Use additive blending for the line and the glow. + + diff --git a/src/Hexagon.js b/src/Hexagon.js new file mode 100644 index 0000000..06ebcd2 --- /dev/null +++ b/src/Hexagon.js @@ -0,0 +1,250 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< // +// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ // +// |__/ // +// Copyright (c) 2021 Simon Schneegans // +// Released under the GPLv3 or later. See LICENSE file for details. // +////////////////////////////////////////////////////////////////////////////////////////// + +'use strict'; + +const GObject = imports.gi.GObject; + +const _ = imports.gettext.domain('burn-my-windows').gettext; + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const utils = Me.imports.src.utils; + +////////////////////////////////////////////////////////////////////////////////////////// +// This effect overlays a glowing hexagonal grid over the window. The grid cells then // +// gradually shrink until the window is fully dissolved. // +////////////////////////////////////////////////////////////////////////////////////////// + +// The shader class for this effect is registered further down in this file. +let Shader = null; + +// The effect class is completely static. It can be used to get some metadata (like the +// effect's name or supported GNOME Shell versions), to initialize the respective page of +// the settings dialog, as well as to create the actual shader for the effect. +var Hexagon = class Hexagon { + + // ---------------------------------------------------------------------------- metadata + + // The effect is available on all GNOME Shell versions supported by this extension. + static getMinShellVersion() { + return [3, 36]; + } + + // This will be called in various places where a unique identifier for this effect is + // required. It should match the prefix of the settings keys which store whether the + // effect is enabled currently (e.g. '*-close-effect'), and its animation time + // (e.g. '*-animation-time'). + static getNick() { + return 'hexagon'; + } + + // This will be shown in the sidebar of the preferences dialog as well as in the + // drop-down menus where the user can choose the effect. + static getLabel() { + return _('Hexagon'); + } + + // -------------------------------------------------------------------- API for prefs.js + + // This is called by the preferences dialog. It loads the settings page for this effect, + // binds all properties to the settings and appends the page to the main stack of the + // preferences dialog. + static getPreferences(dialog) { + + // Add the settings page to the builder. + dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Hexagon.ui`); + + // Bind all properties. + dialog.bindAdjustment('hexagon-animation-time'); + dialog.bindAdjustment('hexagon-scale'); + dialog.bindAdjustment('hexagon-line-width'); + dialog.bindColorButton('hexagon-line-color'); + dialog.bindColorButton('hexagon-glow-color'); + dialog.bindSwitch('hexagon-additive-blending'); + + // Finally, return the new settings page. + return dialog.getBuilder().get_object('hexagon-prefs'); + } + + // ---------------------------------------------------------------- API for extension.js + + // This is called from extension.js whenever a window is closed with this effect. + static createShader(actor, settings, forOpening) { + return new Shader(settings, forOpening); + } + + // The tweakTransition() is called from extension.js to tweak a window's open / close + // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + + // For this effect, windows should neither be scaled nor faded. + static tweakTransition(actor, settings, forOpening) { + return { + 'opacity': {from: 255, to: 255, mode: 1}, + 'scale-x': {from: 1.0, to: 1.0, mode: 1}, + 'scale-y': {from: 1.0, to: 1.0, mode: 1} + }; + } +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// The shader class for this effect will only be registered in GNOME Shell's process // +// (not in the preferences process). It's done this way as Clutter may not be installed // +// on the system and therefore the preferences would crash. // +////////////////////////////////////////////////////////////////////////////////////////// + +if (utils.isInShellProcess()) { + + const Clutter = imports.gi.Clutter; + const shaderSnippets = Me.imports.src.shaderSnippets; + + Shader = GObject.registerClass({}, class Shader extends Clutter.ShaderEffect { + _init(settings, forOpening) { + super._init({shader_type: Clutter.ShaderType.FRAGMENT_SHADER}); + + // Get the two configurable colors. They are directly injected into the shader code + // below. + const glowColor = + Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1]; + const lineColor = + Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1]; + + // If we are currently performing integration test, the animation uses a fixed seed. + const testMode = settings.get_boolean('test-mode'); + + // Feel free to read the inline comments below to learn how this shader works. + this.set_shader_source(` + + // Inject some common shader snippets. + ${shaderSnippets.standardUniforms()} + ${shaderSnippets.noise()} + + const vec2 SEED = vec2(${testMode ? 0 : Math.random()}, + ${testMode ? 0 : Math.random()}); + const float SCALE = 10.0 * ${settings.get_double('hexagon-scale')}; + const float LINE_WIDTH = 0.02 * ${settings.get_double('hexagon-line-width')}; + + // This methods generates a procedural hexagonal pattern. It returns four values: + // result.xy: This contains cell-relative coordinates for the given point. + // [0, 0] is in the center of a cell, [0, 1] at the upper edge, + // [sqrt(4.0 / 3.0), 0] at the right tip and so on. + // result.z: This is the distance to the closest edge. This is used for shrinking + // of the tiles and the sharp overlay lines. + // result.w: This is the distance to the closest cell center. This is used for + // the glow effect. + vec4 getHexagons(vec2 p) { + + // Length of a cell's edge. + const float edgeLength = sqrt(4.0 / 3.0); + + // The hexgrid repeats after this distance. + const vec2 scale = vec2(3.0 * edgeLength, 2.0); + + // This is a repeating grid of scale-sized cells. Y-values are in the + // interval [-1...1], X-value in [-1.5*edgeLength...1.5*edgeLength]. + vec2 a = mod(p, scale) - scale * 0.5; + vec2 aAbs = abs(a); + + // This is the same as above, but offset by half scale. + vec2 b = mod(p + scale * 0.5, scale) - scale * 0.5; + vec2 bAbs = abs(b); + + // Distance to closer edge, diagonally or horizontally. + // Once for cell set A and once for cell set B. + float distA = max(aAbs.x / edgeLength + aAbs.y * 0.5, aAbs.y); + float distB = max(bAbs.x / edgeLength + bAbs.y * 0.5, bAbs.y); + + // Minimum of both is distance to closest edge. + float dist = 1.0 - min(distA, distB); + + // We use the radial distance to the center for glow. + float glow = min(dot(a, a), dot(b, b)) / 1.5; + + // Take cell-relative coordinates from the closer cell. + vec2 cellCoords = distA < distB ? a : b; + + return vec4(cellCoords, dist, glow); + } + + void main() { + + // We simply inverse the progress for opening windows. + float progress = ${forOpening ? '1.0-uProgress' : 'uProgress'}; + + // Add some smooth noise to the progress so that not every tile behaves the + // same. + float noise = simplex2D(cogl_tex_coord_in[0].st + SEED); + progress = clamp(mix(noise - 1.0, noise + 1.0, progress), 0.0, 1.0); + + // glowProgress fades in in the first half of the animation, tileProgress fades + // in in the second half. + float glowProgress = smoothstep(0, 1, clamp(progress / 0.5, 0, 1)); + float tileProgress = smoothstep(0, 1, clamp((progress - 0.5) / 0.5, 0, 1)); + + vec2 texScale = vec2(uSizeX, uSizeY) / SCALE; + vec4 hex = getHexagons(cogl_tex_coord_in[0].st * texScale); + + if (tileProgress > hex.z) { + + // Crop outer parts of the shrinking tiles. + cogl_color_out *= vec4(0.0); + + } else { + + // Make the tiles shrink by offsetting the texture lookup towards the edge + // of the cell. + vec2 lookupOffset = tileProgress * hex.xy / texScale / (1.0 - tileProgress); + cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st + lookupOffset); + + vec4 glow = vec4(${glowColor.red / 255}, ${glowColor.green / 255}, + ${glowColor.blue / 255}, ${glowColor.alpha / 255}); + vec4 line = vec4(${lineColor.red / 255}, ${lineColor.green / 255}, + ${lineColor.blue / 255}, ${lineColor.alpha / 255}); + + // For the glow, we accumulate a few exponentially scaled versions of hex.w. + glow.a *= pow(hex.w, 20.0) * 10.0 + + pow(hex.w, 10.0) * 5.0 + + pow(hex.w, 2.0) * 0.5; + + // Using step(LINE_WIDTH, hex.z) would be simpler, but the below creates some + // fake antialiasing. + line.a *= 1.0 - smoothstep(LINE_WIDTH*0.5, LINE_WIDTH, hex.z); + + // Fade in the glowing lines. + glow.a *= glowProgress; + line.a *= glowProgress; + + // Do not add the hexagon lines onto transparent parts of the window. + glow *= cogl_color_out.a; + line *= cogl_color_out.a; + + if (${settings.get_boolean('hexagon-additive-blending')}) { + cogl_color_out.rgb += glow.rgb * glow.a; + cogl_color_out.rgb += line.rgb * line.a; + } else { + cogl_color_out.rgb = mix(cogl_color_out.rgb, glow.rgb, glow.a); + cogl_color_out.rgb = mix(cogl_color_out.rgb, line.rgb, line.a); + } + } + } + `); + }; + }); +} \ No newline at end of file