More work on porting themes UI to Go
This commit is contained in:
parent
f9b0b54ee5
commit
dd783c842f
49
tools/cmd/themes/list.go
Normal file
49
tools/cmd/themes/list.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package themes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"kitty/tools/themes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
type ThemesList struct {
|
||||||
|
themes, all_themes *themes.Themes
|
||||||
|
current_search string
|
||||||
|
display_strings []string
|
||||||
|
widths []int
|
||||||
|
max_width, current_idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ThemesList) Len() int {
|
||||||
|
if self.themes == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return self.themes.Len()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ThemesList) Next(delta int, allow_wrapping bool) bool {
|
||||||
|
if len(self.display_strings) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
idx := self.current_idx + delta
|
||||||
|
if !allow_wrapping && (idx < 0 || idx > self.Len()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for idx < 0 {
|
||||||
|
idx += self.Len()
|
||||||
|
}
|
||||||
|
self.current_idx = idx % self.Len()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ThemesList) UpdateThemes(themes *themes.Themes) {
|
||||||
|
self.themes, self.all_themes = themes, themes
|
||||||
|
if self.current_search != "" {
|
||||||
|
self.themes = self.all_themes.Copy()
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"kitty/tools/cli"
|
"kitty/tools/cli"
|
||||||
"kitty/tools/themes"
|
"kitty/tools/themes"
|
||||||
|
"kitty/tools/tui/loop"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,6 +55,35 @@ func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
|
|||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
return non_interactive(opts, args[0])
|
return non_interactive(opts, args[0])
|
||||||
}
|
}
|
||||||
|
lp, err := loop.New()
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
cv := utils.NewCachedValues("unicode-input", &CachedData{Category: "All"})
|
||||||
|
h := &handler{lp: lp, opts: opts, cached_data: cv.Load()}
|
||||||
|
defer cv.Save()
|
||||||
|
lp.OnInitialize = func() (string, error) {
|
||||||
|
lp.AllowLineWrapping(false)
|
||||||
|
lp.SetWindowTitle(`Choose a theme for kitty`)
|
||||||
|
h.initialize()
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
lp.OnWakeup = h.on_wakeup
|
||||||
|
lp.OnFinalize = func() string {
|
||||||
|
h.finalize()
|
||||||
|
lp.SetCursorVisible(true)
|
||||||
|
return ``
|
||||||
|
}
|
||||||
|
err = lp.Run()
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
ds := lp.DeathSignalName()
|
||||||
|
if ds != "" {
|
||||||
|
fmt.Println("Killed by signal: ", ds)
|
||||||
|
lp.KillIfSignalled()
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
tools/cmd/themes/ui.go
Normal file
84
tools/cmd/themes/ui.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
package themes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"kitty/tools/themes"
|
||||||
|
"kitty/tools/tui/loop"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
type State int
|
||||||
|
|
||||||
|
const (
|
||||||
|
FETCHING State = iota
|
||||||
|
BROWSING
|
||||||
|
SEARCHING
|
||||||
|
ACCEPTING
|
||||||
|
)
|
||||||
|
|
||||||
|
type CachedData struct {
|
||||||
|
Recent []string `json:"recent"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type fetch_data struct {
|
||||||
|
themes *themes.Themes
|
||||||
|
err error
|
||||||
|
closer io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
lp *loop.Loop
|
||||||
|
opts *Options
|
||||||
|
cached_data *CachedData
|
||||||
|
|
||||||
|
state State
|
||||||
|
fetch_result chan fetch_data
|
||||||
|
all_themes *themes.Themes
|
||||||
|
themes_closer io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *handler) fetch_themes() {
|
||||||
|
r := fetch_data{}
|
||||||
|
r.themes, r.closer, r.err = themes.LoadThemes(time.Duration(self.opts.CacheAge * float64(time.Hour*24)))
|
||||||
|
self.lp.WakeupMainThread()
|
||||||
|
self.fetch_result <- r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *handler) on_wakeup() error {
|
||||||
|
r := <-self.fetch_result
|
||||||
|
if r.err != nil {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
self.state = BROWSING
|
||||||
|
self.all_themes = r.themes
|
||||||
|
self.themes_closer = r.closer
|
||||||
|
self.redraw_after_category_change()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *handler) finalize() {
|
||||||
|
t := self.themes_closer
|
||||||
|
if t != nil {
|
||||||
|
t.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *handler) initialize() {
|
||||||
|
self.fetch_result = make(chan fetch_data)
|
||||||
|
go self.fetch_themes()
|
||||||
|
self.draw_screen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *handler) draw_screen() {
|
||||||
|
// TODO: Implement me
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *handler) redraw_after_category_change() {
|
||||||
|
// TODO: Implement me
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/shirou/gopsutil/v3/process"
|
"github.com/shirou/gopsutil/v3/process"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -713,6 +714,12 @@ type Themes struct {
|
|||||||
index_map []string
|
index_map []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Themes) Copy() *Themes {
|
||||||
|
ans := &Themes{name_map: make(map[string]*Theme, len(self.name_map)), index_map: slices.Clone(self.index_map)}
|
||||||
|
maps.Copy(ans.name_map, self.name_map)
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
var camel_case_pat = (&utils.Once[*regexp.Regexp]{Run: func() *regexp.Regexp {
|
var camel_case_pat = (&utils.Once[*regexp.Regexp]{Run: func() *regexp.Regexp {
|
||||||
return regexp.MustCompile(`([a-z])([A-Z])`)
|
return regexp.MustCompile(`([a-z])([A-Z])`)
|
||||||
}}).Get
|
}}).Get
|
||||||
@ -724,6 +731,8 @@ func theme_name_from_file_name(fname string) string {
|
|||||||
return strings.Join(utils.Map(strings.Title, strings.Split(fname, " ")), " ")
|
return strings.Join(utils.Map(strings.Title, strings.Split(fname, " ")), " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Themes) Len() int { return len(self.name_map) }
|
||||||
|
|
||||||
func (self *Themes) AddFromFile(path string) (*Theme, error) {
|
func (self *Themes) AddFromFile(path string) (*Theme, error) {
|
||||||
m, conf, err := parse_theme_metadata(path)
|
m, conf, err := parse_theme_metadata(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -810,6 +819,38 @@ func (self *Themes) ThemeByName(name string) *Theme {
|
|||||||
return ans
|
return ans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func match(expression string, items []string) []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Themes) ApplySearch(expression string, marks ...string) []string {
|
||||||
|
mark_before, mark_after := "\033[33m", "\033[39m"
|
||||||
|
if len(marks) == 2 {
|
||||||
|
mark_before, mark_after = marks[0], marks[1]
|
||||||
|
}
|
||||||
|
results := match(expression, maps.Keys(self.name_map))
|
||||||
|
name_map := make(map[string]*Theme, len(results))
|
||||||
|
ans := make([]string, 0, len(results))
|
||||||
|
for _, r := range results {
|
||||||
|
pos, k, _ := strings.Cut(r, ":")
|
||||||
|
positions := []int{}
|
||||||
|
for _, q := range strings.Split(pos, ",") {
|
||||||
|
i, _ := strconv.Atoi(q)
|
||||||
|
positions = append(positions, i)
|
||||||
|
text := k
|
||||||
|
for i := len(positions) - 1; i >= 0; i-- {
|
||||||
|
p := positions[i]
|
||||||
|
text = text[:p] + mark_before + text[p:p+1] + mark_after + text[p+1:]
|
||||||
|
}
|
||||||
|
name_map[k] = self.name_map[k]
|
||||||
|
ans = append(ans, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.name_map = name_map
|
||||||
|
self.index_map = maps.Keys(name_map)
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
func LoadThemes(cache_age time.Duration) (ans *Themes, closer io.Closer, err error) {
|
func LoadThemes(cache_age time.Duration) (ans *Themes, closer io.Closer, err error) {
|
||||||
zip_path, err := FetchCached(cache_age)
|
zip_path, err := FetchCached(cache_age)
|
||||||
ans = &Themes{name_map: make(map[string]*Theme)}
|
ans = &Themes{name_map: make(map[string]*Theme)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user