summaryrefslogtreecommitdiff
path: root/gui/internal/model/prefs.go
blob: 9ae79a010f35bbec8210954e070c474eba0b915a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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
}