diff --git a/TODO.md b/TODO.md index 8921cf6..7b68932 100644 --- a/TODO.md +++ b/TODO.md @@ -36,12 +36,12 @@ - [ ] Verify zone calculations work correctly with display scaling - [ ] May need to use `get_resource_scale()` or similar -### 5. Edit Existing Custom Layouts +### 5. Edit Existing Custom Layouts ✅ COMPLETED **Priority: Medium** -- [ ] Currently can only create new layouts in editor -- [ ] Add ability to select and edit existing custom layouts -- [ ] Add UI to choose which layout to edit -- [ ] Pre-populate editor with existing zones when editing +- [x] Currently can only create new layouts in editor +- [x] Add ability to select and edit existing custom layouts +- [x] Add UI to choose which layout to edit +- [x] Pre-populate editor with existing zones when editing ### 15. Show Zone Dimensions in Editor ✅ COMPLETED **Priority: Medium** diff --git a/extension.js b/extension.js index 19cef23..59833dc 100644 --- a/extension.js +++ b/extension.js @@ -66,14 +66,193 @@ ZoneEditor.prototype = { this.resizingZoneIndex = -1; this.resizeEdge = null; // 'n', 's', 'e', 'w', 'ne', 'nw', 'se', 'sw' this.originalZoneBounds = null; + this.editingLayoutId = null; // Track if editing existing layout + this.editingLayoutName = null; + this.selectorOverlay = null; // Layout selector UI + }, + + showLayoutSelector: function() { + if (this.isEditing || this.selectorOverlay) return; + + let monitor = Main.layoutManager.primaryMonitor; + + // Create selector overlay + this.selectorOverlay = new St.Widget({ + style_class: 'gridsnap-selector-overlay', + reactive: true, + x: monitor.x, + y: monitor.y, + width: monitor.width, + height: monitor.height + }); + + this.selectorOverlay.set_style( + 'background-color: rgba(0, 0, 0, 0.7);' + ); + + // Create container for buttons + let buttonContainer = new St.BoxLayout({ + vertical: true, + x: monitor.width / 2 - 200, + y: monitor.height / 2 - 200, + width: 400 + }); + buttonContainer.set_style( + 'background-color: rgba(30, 30, 30, 0.95);' + + 'padding: 20px;' + + 'border-radius: 10px;' + + 'spacing: 10px;' + ); + + // Add title + let title = new St.Label({ + text: 'GridSnap Zone Editor' + }); + title.set_style( + 'color: white;' + + 'font-size: 24px;' + + 'font-weight: bold;' + + 'margin-bottom: 10px;' + ); + buttonContainer.add_child(title); + + // Add subtitle + let subtitle = new St.Label({ + text: 'Choose layout to edit or create new:' + }); + subtitle.set_style( + 'color: rgba(255, 255, 255, 0.8);' + + 'font-size: 14px;' + + 'margin-bottom: 20px;' + ); + buttonContainer.add_child(subtitle); + + // Add "Create New Layout" button + let newButton = this._createSelectorButton('+ Create New Layout', null, null); + buttonContainer.add_child(newButton); + + // Add separator + let separator = new St.Widget({ + height: 2 + }); + separator.set_style( + 'background-color: rgba(255, 255, 255, 0.2);' + + 'margin-top: 10px;' + + 'margin-bottom: 10px;' + ); + buttonContainer.add_child(separator); + + // Add buttons for existing custom layouts + if (zoneManager && zoneManager.layouts) { + let hasCustomLayouts = false; + for (let layoutId in zoneManager.layouts) { + if (layoutId.startsWith('custom-')) { + hasCustomLayouts = true; + let layout = zoneManager.layouts[layoutId]; + let button = this._createSelectorButton( + layout.name + ' (' + layout.zones.length + ' zones)', + layoutId, + layout + ); + buttonContainer.add_child(button); + } + } + + if (!hasCustomLayouts) { + let noLayoutsLabel = new St.Label({ + text: 'No custom layouts yet' + }); + noLayoutsLabel.set_style( + 'color: rgba(255, 255, 255, 0.5);' + + 'font-size: 12px;' + + 'font-style: italic;' + + 'margin: 10px 0px;' + ); + buttonContainer.add_child(noLayoutsLabel); + } + } + + // Add cancel button + let cancelButton = new St.Button({ + label: 'Cancel' + }); + cancelButton.set_style( + 'color: white;' + + 'background-color: rgba(100, 100, 100, 0.5);' + + 'padding: 10px 20px;' + + 'border-radius: 5px;' + + 'margin-top: 20px;' + ); + cancelButton.connect('clicked', Lang.bind(this, function() { + this._closeLayoutSelector(); + })); + buttonContainer.add_child(cancelButton); + + this.selectorOverlay.add_child(buttonContainer); + + // Add key press handler for ESC + this.selectorKeyPressId = this.selectorOverlay.connect('key-press-event', + Lang.bind(this, function(actor, event) { + let symbol = event.get_key_symbol(); + if (symbol === Clutter.KEY_Escape) { + this._closeLayoutSelector(); + return Clutter.EVENT_STOP; + } + return Clutter.EVENT_PROPAGATE; + }) + ); + + Main.layoutManager.addChrome(this.selectorOverlay); + Main.pushModal(this.selectorOverlay); + }, + + _createSelectorButton: function(label, layoutId, layoutData) { + let button = new St.Button({ + label: label + }); + button.set_style( + 'color: white;' + + 'background-color: rgba(100, 149, 237, 0.5);' + + 'padding: 15px 20px;' + + 'border-radius: 5px;' + + 'text-align: left;' + ); + button.connect('clicked', Lang.bind(this, function() { + this._closeLayoutSelector(); + this.startEditor(layoutId, layoutData); + })); + return button; + }, + + _closeLayoutSelector: function() { + if (!this.selectorOverlay) return; + + if (this.selectorKeyPressId) { + this.selectorOverlay.disconnect(this.selectorKeyPressId); + } + + Main.popModal(this.selectorOverlay); + Main.layoutManager.removeChrome(this.selectorOverlay); + this.selectorOverlay.destroy(); + this.selectorOverlay = null; }, - startEditor: function() { + startEditor: function(layoutId, layoutData) { if (this.isEditing) return; - + this.isEditing = true; - this.zones = []; - + + // If editing existing layout, load it + if (layoutId && layoutData) { + this.editingLayoutId = layoutId; + this.editingLayoutName = layoutData.name; + this.zones = JSON.parse(JSON.stringify(layoutData.zones)); // Deep copy + } else { + this.editingLayoutId = null; + this.editingLayoutName = null; + this.zones = []; + } + let monitor = Main.layoutManager.primaryMonitor; // Create editor overlay @@ -116,10 +295,49 @@ ZoneEditor.prototype = { Main.layoutManager.addChrome(this.editorOverlay); Main.pushModal(this.editorOverlay); - + + // If editing existing layout, create zone actors for existing zones + if (this.editingLayoutId && this.zones.length > 0) { + this._createZoneActorsFromData(monitor); + } + // Setup key listener for save/cancel this._setupEditorKeys(); }, + + _createZoneActorsFromData: function(monitor) { + // Create zone actors from existing zone data + this.zoneActors = []; + this.zones.forEach((zone, index) => { + let zoneActor = new St.Widget({ + style_class: 'gridsnap-editor-zone', + x: zone.x * monitor.width, + y: zone.y * monitor.height, + width: zone.width * monitor.width, + height: zone.height * monitor.height + }); + zoneActor.set_style( + 'border: 3px solid rgba(100, 255, 100, 0.9);' + + 'background-color: rgba(100, 255, 100, 0.3);' + + 'border-radius: 4px;' + ); + + let label = new St.Label({ + text: String(index + 1), + x: 10, + y: 10 + }); + label.set_style( + 'color: white;' + + 'font-size: 24px;' + + 'font-weight: bold;' + + 'text-shadow: 2px 2px 4px rgba(0,0,0,0.8);' + ); + zoneActor.add_child(label); + this.editorOverlay.add_child(zoneActor); + this.zoneActors.push(zoneActor); + }); + }, _setupEditorKeys: function() { this.keyPressId = this.editorOverlay.connect('key-press-event', @@ -571,12 +789,21 @@ ZoneEditor.prototype = { this._cancelEditor(); return; } - - // Generate layout name - let layoutId = 'custom-' + Date.now(); - let layoutName = 'Custom Layout (' + this.zones.length + ' zones)'; - - // Add to zoneManager's layouts + + let layoutId, layoutName; + + // Check if editing existing layout or creating new + if (this.editingLayoutId) { + // Update existing layout + layoutId = this.editingLayoutId; + layoutName = this.editingLayoutName + ' (updated)'; + } else { + // Create new layout + layoutId = 'custom-' + Date.now(); + layoutName = 'Custom Layout (' + this.zones.length + ' zones)'; + } + + // Save to zoneManager's layouts if (zoneManager) { zoneManager.layouts[layoutId] = { name: layoutName, @@ -587,9 +814,13 @@ ZoneEditor.prototype = { // Persist to file for permanent storage zoneManager._saveLayouts(); - Main.notify('GridSnap', 'Layout saved: ' + layoutName); + if (this.editingLayoutId) { + Main.notify('GridSnap', 'Layout updated: ' + layoutName); + } else { + Main.notify('GridSnap', 'Layout saved: ' + layoutName); + } } - + this._cancelEditor(); }, @@ -678,7 +909,7 @@ ZoneManager.prototype = { Main.keybindingManager.addHotKey( 'open-zone-editor', 'e', - Lang.bind(this, function() { this.editor.startEditor(); }) + Lang.bind(this, function() { this.editor.showLayoutSelector(); }) ); // Add keybindings for quick snap to zones (Super+Ctrl+1-9)