diff --git a/TODO.md b/TODO.md index 865e214..95a0502 100644 --- a/TODO.md +++ b/TODO.md @@ -2,14 +2,14 @@ ## Critical Issues -### 1. Custom Layout Persistence (extension.js:297) +### 1. Custom Layout Persistence ✅ COMPLETED **Priority: High** -- [ ] Custom layouts are only stored in memory -- [ ] Layouts are lost when extension reloads or Cinnamon restarts -- [ ] Need to implement file-based storage -- [ ] Location: `~/.config/gridsnap/layouts.json` or similar -- [ ] Save layouts when created in graphical editor -- [ ] Load layouts on extension initialization +- [x] Custom layouts are only stored in memory +- [x] Layouts are lost when extension reloads or Cinnamon restarts +- [x] Need to implement file-based storage +- [x] Location: `~/.local/share/gridsnap/layouts.json` +- [x] Save layouts when created in graphical editor +- [x] Load layouts on extension initialization ### 2. Remove Last Zone Implementation (extension.js:236-274) **Priority: Medium** diff --git a/extension.js b/extension.js index ee421b8..860848d 100644 --- a/extension.js +++ b/extension.js @@ -5,9 +5,15 @@ const Clutter = imports.gi.Clutter; const Lang = imports.lang; const Mainloop = imports.mainloop; const Settings = imports.ui.settings; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; let zoneManager; +// Storage path for custom layouts +const STORAGE_DIR = GLib.get_user_data_dir() + '/gridsnap'; +const LAYOUTS_FILE = STORAGE_DIR + '/layouts.json'; + // Default zone layouts const DEFAULT_LAYOUTS = { 'grid-2x2': { @@ -291,11 +297,11 @@ ZoneEditor.prototype = { zones: this.zones }; zoneManager.currentLayout = layoutId; - + + // Persist to file for permanent storage + zoneManager._saveLayouts(); + Main.notify('GridSnap', 'Layout saved: ' + layoutName); - - // TODO: Persist to file for permanent storage - global.log('GridSnap: Layout saved - ' + JSON.stringify(zoneManager.layouts[layoutId])); } this._cancelEditor(); @@ -330,12 +336,15 @@ ZoneManager.prototype = { _init: function() { this.overlay = null; this.currentLayout = 'grid-2x2'; - this.layouts = DEFAULT_LAYOUTS; + this.layouts = {}; this.isShowing = false; this.dragInProgress = false; this.draggedWindow = null; this.editor = new ZoneEditor(); + // Load layouts (default + saved custom layouts) + this._loadLayouts(); + // Connect to window grab events this._connectSignals(); this._setupKeybindings(); @@ -379,6 +388,58 @@ ZoneManager.prototype = { ); } }, + + _loadLayouts: function() { + // Start with default layouts + for (let layoutId in DEFAULT_LAYOUTS) { + this.layouts[layoutId] = DEFAULT_LAYOUTS[layoutId]; + } + + // Try to load custom layouts from file + try { + let file = Gio.File.new_for_path(LAYOUTS_FILE); + if (file.query_exists(null)) { + let [success, contents] = file.load_contents(null); + if (success) { + let customLayouts = JSON.parse(contents.toString()); + // Merge custom layouts with defaults + for (let layoutId in customLayouts) { + this.layouts[layoutId] = customLayouts[layoutId]; + } + global.log('GridSnap: Loaded ' + Object.keys(customLayouts).length + ' custom layout(s)'); + } + } + } catch(e) { + global.logError('GridSnap: Error loading custom layouts - ' + e); + } + }, + + _saveLayouts: function() { + try { + // Create directory if it doesn't exist + let dir = Gio.File.new_for_path(STORAGE_DIR); + if (!dir.query_exists(null)) { + dir.make_directory_with_parents(null); + } + + // Extract only custom layouts (those starting with 'custom-') + let customLayouts = {}; + for (let layoutId in this.layouts) { + if (layoutId.startsWith('custom-')) { + customLayouts[layoutId] = this.layouts[layoutId]; + } + } + + // Save to file + let file = Gio.File.new_for_path(LAYOUTS_FILE); + let contents = JSON.stringify(customLayouts, null, 2); + file.replace_contents(contents, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null); + + global.log('GridSnap: Saved ' + Object.keys(customLayouts).length + ' custom layout(s) to ' + LAYOUTS_FILE); + } catch(e) { + global.logError('GridSnap: Error saving custom layouts - ' + e); + } + }, _checkModifiersDuringDrag: function() { if (!this.dragInProgress) {