// SPDX-License-Identifier: GPL-3.0-or-later package model import ( "encoding/json" "os" "path/filepath" "git.labunix.xyz/krino/internal/xdg" ) // Prefs is how the window behaves - nothing about what krino does to // files, which belongs in the configuration. It lives beside krino.conf as // gui.json, a file krino itself never reads. type Prefs struct { // Colours paints the configuration in the Text tab. Colours bool `json:"colours"` // Preview shows the file behind the selected row in the Plan tab. Preview bool `json:"preview"` // SelectAll starts a plan's rows checked, as the terminal review does. SelectAll bool `json:"select_all"` // PreviewHeight is how tall the preview under the explanation is, in // pixels; the divider between them sets it, and so does Settings. PreviewHeight int `json:"preview_height"` // PreviewWidth is the same measurement in the stacked layout, where // the preview sits beside the explanation rather than under it. PreviewWidth int `json:"preview_width"` // ListWidth and ListHeight are where the divider between the file list // and the explanation was left, in each layout. Every divider a hand // moves is kept. ListWidth int `json:"list_width"` ListHeight int `json:"list_height"` // ShowSize, ShowAge and ShowRule are the columns that can be turned off // when the window is narrow or they are not wanted. ShowSize bool `json:"show_size"` ShowAge bool `json:"show_age"` ShowRule bool `json:"show_rule"` // Sort is the order a freshly scanned plan is read in; the picker in // the toolbar changes it for the window in front of you. Sort string `json:"sort"` // Layout is how the Plan tab is arranged: "side" puts the file list // beside the explanation, "stacked" puts it above, with the preview // beside the explanation underneath. Layout string `json:"layout"` } // The two arrangements of the Plan tab. const ( LayoutSide = "side" LayoutStacked = "stacked" ) // The sizes a window opens with until a divider is moved, and the floor a // smaller one is raised to. const ( DefaultPreviewHeight = 280 DefaultPreviewWidth = 360 DefaultListWidth = 780 DefaultListHeight = 420 ) // DefaultPrefs is what a window does before anything is chosen. func DefaultPrefs() Prefs { return Prefs{Colours: true, Preview: true, SelectAll: true, ShowSize: true, ShowAge: true, ShowRule: true, Sort: SortDefault, PreviewHeight: DefaultPreviewHeight, PreviewWidth: DefaultPreviewWidth, ListWidth: DefaultListWidth, ListHeight: DefaultListHeight, Layout: LayoutSide} } // PrefsFile is where they are kept. func PrefsFile() string { return filepath.Join(xdg.ConfigHome(), "krino", "gui.json") } // LoadPrefs reads them. A missing or unreadable file is not an error: the // window opens with the defaults, as it does the first time. func LoadPrefs() Prefs { p := DefaultPrefs() data, err := os.ReadFile(PrefsFile()) if err != nil { return p } if err := json.Unmarshal(data, &p); err != nil { return DefaultPrefs() } if p.PreviewHeight < 80 { p.PreviewHeight = DefaultPreviewHeight } if p.PreviewWidth < 80 { p.PreviewWidth = DefaultPreviewWidth } if p.Layout != LayoutSide && p.Layout != LayoutStacked { p.Layout = LayoutSide } if !IsSortOrder(p.Sort) { p.Sort = SortDefault } return p } // Save writes them, creating the directory if it is not there yet. func (p Prefs) Save() error { data, err := json.MarshalIndent(p, "", " ") if err != nil { return err } file := PrefsFile() if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil { return err } return os.WriteFile(file, append(data, '\n'), 0o644) } // Display is everything the Settings window can change about the window // itself. It deliberately leaves out the divider positions: those are set // by dragging, and a settings change must not disturb them. Composing a // whole Prefs in the Settings window instead is what used to write // PreviewWidth, ListWidth and ListHeight back as zeros, so dragging the // panes to taste and then ticking any checkbox threw the panes away. type Display struct { Sort string Layout string ShowSize bool ShowAge bool ShowRule bool Colours bool Preview bool SelectAll bool PreviewHeight int } // DisplayOf is the part of p the Settings window shows. func (p Prefs) DisplayOf() Display { return Display{ Sort: p.Sort, Layout: p.Layout, ShowSize: p.ShowSize, ShowAge: p.ShowAge, ShowRule: p.ShowRule, Colours: p.Colours, Preview: p.Preview, SelectAll: p.SelectAll, PreviewHeight: p.PreviewHeight, } } // WithDisplay is p with d applied and everything else - where each divider // was left - kept as it was. func (p Prefs) WithDisplay(d Display) Prefs { p.Sort, p.Layout = d.Sort, d.Layout p.ShowSize, p.ShowAge, p.ShowRule = d.ShowSize, d.ShowAge, d.ShowRule p.Colours, p.Preview, p.SelectAll = d.Colours, d.Preview, d.SelectAll p.PreviewHeight = d.PreviewHeight return p }