Skip to content

Commit 4c27233

Browse files
authored
Merge pull request #12 from ErikKalkoken/next-version
Add shortcuts & other improvements
2 parents 4fd908a + 554e6a6 commit 4c27233

File tree

10 files changed

+154
-136
lines changed

10 files changed

+154
-136
lines changed

FyneApp.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Website = "https://github.com/ErikKalkoken/janice"
44
Icon = "icon.png"
55
Name = "Janice"
66
ID = "io.github.erikkalkoken.janice"
7-
Version = "0.5.0"
7+
Version = "0.6.0"
88
Build = 1
99

1010
[Release]

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.23
44

55
require (
66
fyne.io/fyne/v2 v2.5.2
7-
github.com/ErikKalkoken/fyne-kx v0.0.0-20241017191228-704cdd115b48
7+
github.com/ErikKalkoken/fyne-kx v0.0.0-20241025174815-e140dbe057e4
88
github.com/dweymouth/fyne-tooltip v0.2.1
99
github.com/hashicorp/go-version v1.7.0
1010
github.com/jarcoal/httpmock v1.3.1
@@ -38,7 +38,6 @@ require (
3838
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
3939
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
4040
github.com/yuin/goldmark v1.7.8 // indirect
41-
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
4241
golang.org/x/image v0.21.0 // indirect
4342
golang.org/x/mobile v0.0.0-20241016134751-7ff83004ec2c // indirect
4443
golang.org/x/net v0.30.0 // indirect

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
4545
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
4646
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
4747
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
48-
github.com/ErikKalkoken/fyne-kx v0.0.0-20241017191228-704cdd115b48 h1:LYF0lAShpyI+MXGp6fHe0V/2o+Lu8m9rmK88JUInTnQ=
49-
github.com/ErikKalkoken/fyne-kx v0.0.0-20241017191228-704cdd115b48/go.mod h1:T1f84yGlDtqUUhjdSHVhX9yhzvFCxNLdsj08lUxkdww=
48+
github.com/ErikKalkoken/fyne-kx v0.0.0-20241025174815-e140dbe057e4 h1:7JZ55BPANlRZtMeHevNiFRtLhGYrTenLyoNpQJisGaQ=
49+
github.com/ErikKalkoken/fyne-kx v0.0.0-20241025174815-e140dbe057e4/go.mod h1:N/YVe+viCUiSOr2TziRV7s9iEOlIfS/n/s5r5LiPcXg=
5050
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
5151
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
5252
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -333,8 +333,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
333333
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
334334
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
335335
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
336-
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
337-
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
338336
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
339337
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
340338
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=

internal/ui/menu.go

Lines changed: 103 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"fyne.io/fyne/v2"
1010
"fyne.io/fyne/v2/dialog"
11+
"fyne.io/fyne/v2/driver/desktop"
1112
"fyne.io/fyne/v2/storage"
1213
"github.com/ErikKalkoken/janice/internal/jsondocument"
1314
)
@@ -18,48 +19,37 @@ const (
1819
)
1920

2021
func (u *UI) makeMenu() *fyne.MainMenu {
21-
recentItem := fyne.NewMenuItem("Open Recent", nil)
22-
recentItem.ChildMenu = fyne.NewMenu("")
22+
// File menu
23+
openRecentItem := fyne.NewMenuItem("Open Recent", nil)
24+
openRecentItem.ChildMenu = fyne.NewMenu("")
25+
26+
fileSettingsItem := fyne.NewMenuItem("Settings...", u.showSettingsDialog)
27+
fileSettingsItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyComma, Modifier: fyne.KeyModifierControl}
28+
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileSettingsItem))
29+
30+
fileReloadItem := fyne.NewMenuItem("Reload", u.fileReload)
31+
fileReloadItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyR, Modifier: fyne.KeyModifierAlt}
32+
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileReloadItem))
33+
34+
fileOpenItem := fyne.NewMenuItem("Open File...", u.fileOpen)
35+
fileOpenItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyO, Modifier: fyne.KeyModifierControl}
36+
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileOpenItem))
37+
38+
fileNewItem := fyne.NewMenuItem("New", u.fileNew)
39+
fileNewItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyN, Modifier: fyne.KeyModifierControl}
40+
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileNewItem))
41+
2342
u.fileMenu = fyne.NewMenu("File",
24-
fyne.NewMenuItem("New", func() {
25-
u.reset()
26-
}),
43+
fileNewItem,
2744
fyne.NewMenuItemSeparator(),
28-
fyne.NewMenuItem("Open File...", func() {
29-
d := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
30-
if err != nil {
31-
u.showErrorDialog("Failed to read folder", err)
32-
return
33-
}
34-
if reader == nil {
35-
return
36-
}
37-
u.loadDocument(reader)
38-
}, u.window)
39-
d.Show()
40-
filterEnabled := u.app.Preferences().BoolWithFallback(settingExtensionFilter, settingExtensionDefault)
41-
if filterEnabled {
42-
f := storage.NewExtensionFileFilter([]string{".json"})
43-
d.SetFilter(f)
44-
}
45-
}),
46-
recentItem,
45+
fileOpenItem,
46+
openRecentItem,
4747
fyne.NewMenuItem("Open From Clipboard", func() {
4848
r := strings.NewReader(u.window.Clipboard().Content())
4949
reader := jsondocument.MakeURIReadCloser(r, "CLIPBOARD")
5050
u.loadDocument(reader)
5151
}),
52-
fyne.NewMenuItem("Reload", func() {
53-
if u.currentFile == nil {
54-
return
55-
}
56-
reader, err := storage.Reader(u.currentFile)
57-
if err != nil {
58-
u.showErrorDialog("Failed to reload file", err)
59-
return
60-
}
61-
u.loadDocument(reader)
62-
}),
52+
fileReloadItem,
6353
fyne.NewMenuItemSeparator(),
6454
fyne.NewMenuItem("Export Selection To File...", func() {
6555
byt, err := u.extractSelection()
@@ -91,10 +81,10 @@ func (u *UI) makeMenu() *fyne.MainMenu {
9181
u.window.Clipboard().SetContent(string(byt))
9282
}),
9383
fyne.NewMenuItemSeparator(),
94-
fyne.NewMenuItem("Settings...", func() {
95-
u.showSettingsDialog()
96-
}),
84+
fileSettingsItem,
9785
)
86+
87+
// View menu
9888
toogleSelectionFrame := fyne.NewMenuItem("Show selected element", func() {
9989
u.toogleViewSelection()
10090
})
@@ -104,16 +94,6 @@ func (u *UI) makeMenu() *fyne.MainMenu {
10494
})
10595
toogleDetailFrame.Checked = u.value.isShown()
10696
u.viewMenu = fyne.NewMenu("View",
107-
fyne.NewMenuItem("Scroll to top", func() {
108-
u.treeWidget.ScrollToTop()
109-
}),
110-
fyne.NewMenuItem("Scroll to bottom", func() {
111-
u.treeWidget.ScrollToBottom()
112-
}),
113-
fyne.NewMenuItem("Scroll to selection", func() {
114-
u.scrollTo(u.selection.selectedUID)
115-
}),
116-
fyne.NewMenuItemSeparator(),
11797
fyne.NewMenuItem("Expand All", func() {
11898
u.treeWidget.OpenAllBranches()
11999
}),
@@ -124,6 +104,25 @@ func (u *UI) makeMenu() *fyne.MainMenu {
124104
toogleSelectionFrame,
125105
toogleDetailFrame,
126106
)
107+
108+
// Go menu
109+
goTopItem := fyne.NewMenuItem("Go to top", u.treeWidget.ScrollToTop)
110+
goTopItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyHome, Modifier: fyne.KeyModifierControl}
111+
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(goTopItem))
112+
113+
goBottomItem := fyne.NewMenuItem("Go to bottom", u.treeWidget.ScrollToBottom)
114+
goBottomItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyEnd, Modifier: fyne.KeyModifierControl}
115+
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(goBottomItem))
116+
117+
u.goMenu = fyne.NewMenu("Go",
118+
goTopItem,
119+
goBottomItem,
120+
fyne.NewMenuItem("Go to selection", func() {
121+
u.scrollTo(u.selection.selectedUID)
122+
}),
123+
)
124+
125+
// Help menu
127126
helpMenu := fyne.NewMenu("Help",
128127
fyne.NewMenuItem("Report a bug", func() {
129128
url, _ := url.Parse(websiteURL + "/issues")
@@ -134,10 +133,53 @@ func (u *UI) makeMenu() *fyne.MainMenu {
134133
u.showAboutDialog()
135134
}),
136135
)
137-
main := fyne.NewMainMenu(u.fileMenu, u.viewMenu, helpMenu)
136+
137+
main := fyne.NewMainMenu(u.fileMenu, u.viewMenu, u.goMenu, helpMenu)
138138
return main
139139
}
140140

141+
func (u *UI) fileOpen() {
142+
d := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
143+
if err != nil {
144+
u.showErrorDialog("Failed to read folder", err)
145+
return
146+
}
147+
if reader == nil {
148+
return
149+
}
150+
u.loadDocument(reader)
151+
}, u.window)
152+
d.Show()
153+
filterEnabled := u.app.Preferences().BoolWithFallback(settingExtensionFilter, settingExtensionDefault)
154+
if filterEnabled {
155+
f := storage.NewExtensionFileFilter([]string{".json"})
156+
d.SetFilter(f)
157+
}
158+
}
159+
160+
// fileNew resets the app to it's initial state
161+
func (u *UI) fileNew() {
162+
u.document.Reset()
163+
u.setTitle("")
164+
u.statusBar.reset()
165+
u.welcomeMessage.Show()
166+
u.toogleHasDocument(false)
167+
u.selection.reset()
168+
u.value.reset()
169+
}
170+
171+
func (u *UI) fileReload() {
172+
if u.currentFile == nil {
173+
return
174+
}
175+
reader, err := storage.Reader(u.currentFile)
176+
if err != nil {
177+
u.showErrorDialog("Failed to reload file", err)
178+
return
179+
}
180+
u.loadDocument(reader)
181+
}
182+
141183
func (u *UI) extractSelection() ([]byte, error) {
142184
uid := u.selection.selectedUID
143185
n := u.document.Value(uid)
@@ -224,3 +266,15 @@ func (u *UI) toogleViewDetail() {
224266
menuItem.Checked = u.value.isShown()
225267
u.viewMenu.Refresh()
226268
}
269+
270+
// addShortcutFromMenuItem is a helper for defining shortcuts.
271+
// It allows to add an already defined shortcut from a menu item to the canvas.
272+
//
273+
// For example:
274+
//
275+
// window.Canvas().AddShortcut(menuItem)
276+
func addShortcutFromMenuItem(item *fyne.MenuItem) (fyne.Shortcut, func(fyne.Shortcut)) {
277+
return item.Shortcut, func(s fyne.Shortcut) {
278+
item.Action()
279+
}
280+
}

internal/ui/searchbar.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var type2importance = map[jsondocument.JSONType]widget.Importance{
3636
// searchBarFrame represents the search bar frame in the UI.
3737
type searchBarFrame struct {
3838
content *fyne.Container
39-
ui *UI
39+
u *UI
4040

4141
searchEntry *widget.Entry
4242
searchButton *ttwidget.Button
@@ -48,7 +48,7 @@ type searchBarFrame struct {
4848

4949
func (u *UI) newSearchBarFrame() *searchBarFrame {
5050
f := &searchBarFrame{
51-
ui: u,
51+
u: u,
5252
searchEntry: widget.NewEntry(),
5353
}
5454
// search frame
@@ -71,15 +71,15 @@ func (u *UI) newSearchBarFrame() *searchBarFrame {
7171
})
7272
f.searchButton.SetToolTip("Search")
7373
f.scrollBottom = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceVerticalalignbottomSvg), func() {
74-
f.ui.treeWidget.ScrollToBottom()
74+
f.u.treeWidget.ScrollToBottom()
7575
})
7676
f.scrollBottom.SetToolTip("Scroll to bottom")
7777
f.scrollTop = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceVerticalaligntopSvg), func() {
78-
f.ui.treeWidget.ScrollToTop()
78+
f.u.treeWidget.ScrollToTop()
7979
})
8080
f.scrollTop.SetToolTip("Scroll to top")
8181
f.collapseAll = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceUnfoldlessSvg), func() {
82-
f.ui.treeWidget.CloseAllBranches()
82+
f.u.treeWidget.CloseAllBranches()
8383
})
8484
f.collapseAll.SetToolTip("Collapse all")
8585
c := container.NewBorder(
@@ -131,7 +131,7 @@ func (f *searchBarFrame) doSearch() {
131131
b := widget.NewButton("Cancel", func() {
132132
cancel()
133133
})
134-
d := dialog.NewCustomWithoutButtons("Search", container.NewVBox(c, b), f.ui.window)
134+
d := dialog.NewCustomWithoutButtons("Search", container.NewVBox(c, b), f.u.window)
135135
d.Show()
136136
d.SetOnClosed(func() {
137137
cancel()
@@ -146,30 +146,30 @@ func (f *searchBarFrame) doSearch() {
146146
search = strings.ToLower(search)
147147
if search != "true" && search != "false" && search != "null" {
148148
d.Hide()
149-
f.ui.showErrorDialog("Allowed keywords are: true, false, null", nil)
149+
f.u.showErrorDialog("Allowed keywords are: true, false, null", nil)
150150
return
151151
}
152152
case searchTypeString:
153153
typ = jsondocument.SearchString
154154
case searchTypeNumber:
155155
typ = jsondocument.SearchNumber
156156
}
157-
uid, err := f.ui.document.Search(ctx, f.ui.selection.selectedUID, search, typ)
157+
uid, err := f.u.document.Search(ctx, f.u.selection.selectedUID, search, typ)
158158
d.Hide()
159159
if errors.Is(err, jsondocument.ErrCallerCanceled) {
160160
return
161161
} else if errors.Is(err, jsondocument.ErrNotFound) {
162162
d2 := dialog.NewInformation(
163163
"No match",
164164
fmt.Sprintf("No %s found matching %s", searchType, search),
165-
f.ui.window,
165+
f.u.window,
166166
)
167167
d2.Show()
168168
return
169169
} else if err != nil {
170-
f.ui.showErrorDialog("Search failed", err)
170+
f.u.showErrorDialog("Search failed", err)
171171
return
172172
}
173-
f.ui.scrollTo(uid)
173+
f.u.scrollTo(uid)
174174
}()
175175
}

internal/ui/selection.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
// selection represents the selection frame in the UI.
1616
type selectionFrame struct {
1717
content *fyne.Container
18-
ui *UI
18+
u *UI
1919

2020
selectedUID widget.TreeNodeID
2121
selectedPath *fyne.Container
@@ -27,7 +27,7 @@ func (u *UI) newSelectionFrame() *selectionFrame {
2727
myHBox := layout.NewCustomPaddedHBoxLayout(-5)
2828

2929
f := &selectionFrame{
30-
ui: u,
30+
u: u,
3131
selectedPath: container.New(myHBox),
3232
}
3333
f.jumpToSelection = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceReadmoreSvg), func() {
@@ -88,19 +88,19 @@ type NodePlus struct {
8888

8989
func (f *selectionFrame) set(uid string) {
9090
f.selectedUID = uid
91-
p := f.ui.document.Path(uid)
91+
p := f.u.document.Path(uid)
9292
var path []NodePlus
9393
for _, uid2 := range p {
94-
path = append(path, NodePlus{Node: f.ui.document.Value(uid2), UID: uid2})
94+
path = append(path, NodePlus{Node: f.u.document.Value(uid2), UID: uid2})
9595
}
96-
path = append(path, NodePlus{Node: f.ui.document.Value(uid), UID: uid})
96+
path = append(path, NodePlus{Node: f.u.document.Value(uid), UID: uid})
9797
f.selectedPath.RemoveAll()
9898
for i, n := range path {
9999
isLast := i == len(path)-1
100100
if !isLast {
101101
l := kxwidget.NewTappableLabel(n.Key, func() {
102-
f.ui.scrollTo(n.UID)
103-
f.ui.selectElement(n.UID)
102+
f.u.scrollTo(n.UID)
103+
f.u.selectElement(n.UID)
104104
})
105105
f.selectedPath.Add(l)
106106
} else {

0 commit comments

Comments
 (0)