Add animation effects for smooth user experience
Implemented comprehensive animation effects throughout the extension: 1. Zone Overlay Animations: - Fade-in animation (200ms) when showing zones - Fade-out animation (150ms) when hiding zones - Uses Clutter.AnimationMode.EASE_OUT_QUAD for smooth easing 2. Window Snapping Animations: - Smooth animated window movement (250ms) when snapping to zones - Animates window actor position and size - Falls back to instant move if actor not available - Final position set precisely after animation completes 3. Visual Snap Feedback: - Green flash effect when window snaps to zone - Flash shows the exact zone boundaries - Fades out over 400ms - Works for both Shift+drag and keyboard snapping Implementation details: - Used Clutter's ease() method for all animations - Overlay fade animations prevent jarring appearance/disappearance - Window animations use compositor actor for smooth transitions - Flash feedback uses temporary widget with auto-cleanup - All animations use EASE_OUT_QUAD for natural feel User experience improvements: - Zones appear and disappear smoothly instead of instantly - Windows glide into position rather than jumping - Clear visual confirmation when snap occurs - Professional, polished feel throughout Fixes TODO item #11. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
8
TODO.md
8
TODO.md
@@ -111,11 +111,11 @@
|
||||
- [ ] 1/3 - 2/3 layouts
|
||||
- [ ] Picture-in-picture style layouts
|
||||
|
||||
### 11. Animation Effects
|
||||
### 11. Animation Effects ✅ COMPLETED
|
||||
**Priority: Low**
|
||||
- [ ] Add smooth transitions when snapping windows
|
||||
- [ ] Animate zone overlay appearance/disappearance
|
||||
- [ ] Visual feedback when window snaps to zone
|
||||
- [x] Add smooth transitions when snapping windows
|
||||
- [x] Animate zone overlay appearance/disappearance
|
||||
- [x] Visual feedback when window snaps to zone
|
||||
|
||||
### 12. Settings Panel ✅ COMPLETED
|
||||
**Priority: Medium**
|
||||
|
||||
83
extension.js
83
extension.js
@@ -1071,6 +1071,14 @@ ZoneManager.prototype = {
|
||||
|
||||
Main.layoutManager.addChrome(this.overlay);
|
||||
this.isShowing = true;
|
||||
|
||||
// Fade in animation
|
||||
this.overlay.opacity = 0;
|
||||
this.overlay.ease({
|
||||
opacity: 255,
|
||||
duration: 200,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD
|
||||
});
|
||||
},
|
||||
|
||||
_createZoneActor: function(zone, monitor, index) {
|
||||
@@ -1126,13 +1134,23 @@ ZoneManager.prototype = {
|
||||
_hideZonesOverlay: function() {
|
||||
if (!this.isShowing) return;
|
||||
|
||||
this.isShowing = false;
|
||||
|
||||
if (this.overlay) {
|
||||
// Fade out animation
|
||||
this.overlay.ease({
|
||||
opacity: 0,
|
||||
duration: 150,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => {
|
||||
if (this.overlay) {
|
||||
Main.layoutManager.removeChrome(this.overlay);
|
||||
this.overlay.destroy();
|
||||
this.overlay = null;
|
||||
}
|
||||
|
||||
this.isShowing = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_snapWindowToZone: function(window) {
|
||||
@@ -1159,6 +1177,7 @@ ZoneManager.prototype = {
|
||||
|
||||
if (targetZone) {
|
||||
this._moveWindowToZone(window, targetZone, monitor);
|
||||
this._showSnapFeedback(targetZone, monitor);
|
||||
|
||||
// Show notification if enabled
|
||||
if (this.settings.getValue('notification-on-snap')) {
|
||||
@@ -1181,7 +1200,9 @@ ZoneManager.prototype = {
|
||||
if (zoneIndex >= layout.zones.length) return;
|
||||
|
||||
let monitor = Main.layoutManager.primaryMonitor;
|
||||
this._moveWindowToZone(window, layout.zones[zoneIndex], monitor);
|
||||
let targetZone = layout.zones[zoneIndex];
|
||||
this._moveWindowToZone(window, targetZone, monitor);
|
||||
this._showSnapFeedback(targetZone, monitor);
|
||||
|
||||
// Show notification if enabled
|
||||
if (this.settings.getValue('notification-on-snap')) {
|
||||
@@ -1189,6 +1210,42 @@ ZoneManager.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_showSnapFeedback: function(zone, monitor) {
|
||||
// Create a flash effect on the zone to show snap feedback
|
||||
let x = monitor.x + Math.round(monitor.width * zone.x);
|
||||
let y = monitor.y + Math.round(monitor.height * zone.y);
|
||||
let width = Math.round(monitor.width * zone.width);
|
||||
let height = Math.round(monitor.height * zone.height);
|
||||
|
||||
let flashActor = new St.Widget({
|
||||
style_class: 'gridsnap-flash',
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
|
||||
flashActor.set_style(
|
||||
'border: 4px solid rgba(100, 255, 100, 1.0);' +
|
||||
'background-color: rgba(100, 255, 100, 0.3);' +
|
||||
'border-radius: 4px;'
|
||||
);
|
||||
|
||||
Main.layoutManager.addChrome(flashActor);
|
||||
|
||||
// Animate flash effect
|
||||
flashActor.opacity = 255;
|
||||
flashActor.ease({
|
||||
opacity: 0,
|
||||
duration: 400,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => {
|
||||
Main.layoutManager.removeChrome(flashActor);
|
||||
flashActor.destroy();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_moveWindowToZone: function(window, zone, monitor) {
|
||||
if (!window || !zone || !monitor) return;
|
||||
|
||||
@@ -1206,8 +1263,26 @@ ZoneManager.prototype = {
|
||||
// Use Mainloop.idle_add to defer the operation
|
||||
// This ensures the unmaximize completes before move/resize
|
||||
Mainloop.idle_add(Lang.bind(this, function() {
|
||||
// Move and resize window (false = programmatic operation, not user-initiated)
|
||||
// Get window actor for animation
|
||||
let actor = window.get_compositor_private();
|
||||
if (actor) {
|
||||
// Animate window movement
|
||||
actor.ease({
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height,
|
||||
duration: 250,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => {
|
||||
// Set final position precisely after animation
|
||||
window.move_resize_frame(false, x, y, width, height);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Fallback to instant move if no actor
|
||||
window.move_resize_frame(false, x, y, width, height);
|
||||
}
|
||||
return false; // Don't repeat
|
||||
}));
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user