Skip to content

Commit

Permalink
Merge pull request #12 from ErikKalkoken/next-version
Browse files Browse the repository at this point in the history
Add shortcuts & other improvements
  • Loading branch information
ErikKalkoken authored Oct 25, 2024
2 parents 4fd908a + 554e6a6 commit 4c27233
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 136 deletions.
2 changes: 1 addition & 1 deletion FyneApp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Website = "https://github.com/ErikKalkoken/janice"
Icon = "icon.png"
Name = "Janice"
ID = "io.github.erikkalkoken.janice"
Version = "0.5.0"
Version = "0.6.0"
Build = 1

[Release]
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.23

require (
fyne.io/fyne/v2 v2.5.2
github.com/ErikKalkoken/fyne-kx v0.0.0-20241017191228-704cdd115b48
github.com/ErikKalkoken/fyne-kx v0.0.0-20241025174815-e140dbe057e4
github.com/dweymouth/fyne-tooltip v0.2.1
github.com/hashicorp/go-version v1.7.0
github.com/jarcoal/httpmock v1.3.1
Expand Down Expand Up @@ -38,7 +38,6 @@ require (
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/yuin/goldmark v1.7.8 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/image v0.21.0 // indirect
golang.org/x/mobile v0.0.0-20241016134751-7ff83004ec2c // indirect
golang.org/x/net v0.30.0 // indirect
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ErikKalkoken/fyne-kx v0.0.0-20241017191228-704cdd115b48 h1:LYF0lAShpyI+MXGp6fHe0V/2o+Lu8m9rmK88JUInTnQ=
github.com/ErikKalkoken/fyne-kx v0.0.0-20241017191228-704cdd115b48/go.mod h1:T1f84yGlDtqUUhjdSHVhX9yhzvFCxNLdsj08lUxkdww=
github.com/ErikKalkoken/fyne-kx v0.0.0-20241025174815-e140dbe057e4 h1:7JZ55BPANlRZtMeHevNiFRtLhGYrTenLyoNpQJisGaQ=
github.com/ErikKalkoken/fyne-kx v0.0.0-20241025174815-e140dbe057e4/go.mod h1:N/YVe+viCUiSOr2TziRV7s9iEOlIfS/n/s5r5LiPcXg=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
Expand Down Expand Up @@ -333,8 +333,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
Expand Down
152 changes: 103 additions & 49 deletions internal/ui/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/driver/desktop"
"fyne.io/fyne/v2/storage"
"github.com/ErikKalkoken/janice/internal/jsondocument"
)
Expand All @@ -18,48 +19,37 @@ const (
)

func (u *UI) makeMenu() *fyne.MainMenu {
recentItem := fyne.NewMenuItem("Open Recent", nil)
recentItem.ChildMenu = fyne.NewMenu("")
// File menu
openRecentItem := fyne.NewMenuItem("Open Recent", nil)
openRecentItem.ChildMenu = fyne.NewMenu("")

fileSettingsItem := fyne.NewMenuItem("Settings...", u.showSettingsDialog)
fileSettingsItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyComma, Modifier: fyne.KeyModifierControl}
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileSettingsItem))

fileReloadItem := fyne.NewMenuItem("Reload", u.fileReload)
fileReloadItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyR, Modifier: fyne.KeyModifierAlt}
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileReloadItem))

fileOpenItem := fyne.NewMenuItem("Open File...", u.fileOpen)
fileOpenItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyO, Modifier: fyne.KeyModifierControl}
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileOpenItem))

fileNewItem := fyne.NewMenuItem("New", u.fileNew)
fileNewItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyN, Modifier: fyne.KeyModifierControl}
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(fileNewItem))

u.fileMenu = fyne.NewMenu("File",
fyne.NewMenuItem("New", func() {
u.reset()
}),
fileNewItem,
fyne.NewMenuItemSeparator(),
fyne.NewMenuItem("Open File...", func() {
d := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil {
u.showErrorDialog("Failed to read folder", err)
return
}
if reader == nil {
return
}
u.loadDocument(reader)
}, u.window)
d.Show()
filterEnabled := u.app.Preferences().BoolWithFallback(settingExtensionFilter, settingExtensionDefault)
if filterEnabled {
f := storage.NewExtensionFileFilter([]string{".json"})
d.SetFilter(f)
}
}),
recentItem,
fileOpenItem,
openRecentItem,
fyne.NewMenuItem("Open From Clipboard", func() {
r := strings.NewReader(u.window.Clipboard().Content())
reader := jsondocument.MakeURIReadCloser(r, "CLIPBOARD")
u.loadDocument(reader)
}),
fyne.NewMenuItem("Reload", func() {
if u.currentFile == nil {
return
}
reader, err := storage.Reader(u.currentFile)
if err != nil {
u.showErrorDialog("Failed to reload file", err)
return
}
u.loadDocument(reader)
}),
fileReloadItem,
fyne.NewMenuItemSeparator(),
fyne.NewMenuItem("Export Selection To File...", func() {
byt, err := u.extractSelection()
Expand Down Expand Up @@ -91,10 +81,10 @@ func (u *UI) makeMenu() *fyne.MainMenu {
u.window.Clipboard().SetContent(string(byt))
}),
fyne.NewMenuItemSeparator(),
fyne.NewMenuItem("Settings...", func() {
u.showSettingsDialog()
}),
fileSettingsItem,
)

// View menu
toogleSelectionFrame := fyne.NewMenuItem("Show selected element", func() {
u.toogleViewSelection()
})
Expand All @@ -104,16 +94,6 @@ func (u *UI) makeMenu() *fyne.MainMenu {
})
toogleDetailFrame.Checked = u.value.isShown()
u.viewMenu = fyne.NewMenu("View",
fyne.NewMenuItem("Scroll to top", func() {
u.treeWidget.ScrollToTop()
}),
fyne.NewMenuItem("Scroll to bottom", func() {
u.treeWidget.ScrollToBottom()
}),
fyne.NewMenuItem("Scroll to selection", func() {
u.scrollTo(u.selection.selectedUID)
}),
fyne.NewMenuItemSeparator(),
fyne.NewMenuItem("Expand All", func() {
u.treeWidget.OpenAllBranches()
}),
Expand All @@ -124,6 +104,25 @@ func (u *UI) makeMenu() *fyne.MainMenu {
toogleSelectionFrame,
toogleDetailFrame,
)

// Go menu
goTopItem := fyne.NewMenuItem("Go to top", u.treeWidget.ScrollToTop)
goTopItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyHome, Modifier: fyne.KeyModifierControl}
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(goTopItem))

goBottomItem := fyne.NewMenuItem("Go to bottom", u.treeWidget.ScrollToBottom)
goBottomItem.Shortcut = &desktop.CustomShortcut{KeyName: fyne.KeyEnd, Modifier: fyne.KeyModifierControl}
u.window.Canvas().AddShortcut(addShortcutFromMenuItem(goBottomItem))

u.goMenu = fyne.NewMenu("Go",
goTopItem,
goBottomItem,
fyne.NewMenuItem("Go to selection", func() {
u.scrollTo(u.selection.selectedUID)
}),
)

// Help menu
helpMenu := fyne.NewMenu("Help",
fyne.NewMenuItem("Report a bug", func() {
url, _ := url.Parse(websiteURL + "/issues")
Expand All @@ -134,10 +133,53 @@ func (u *UI) makeMenu() *fyne.MainMenu {
u.showAboutDialog()
}),
)
main := fyne.NewMainMenu(u.fileMenu, u.viewMenu, helpMenu)

main := fyne.NewMainMenu(u.fileMenu, u.viewMenu, u.goMenu, helpMenu)
return main
}

func (u *UI) fileOpen() {
d := dialog.NewFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil {
u.showErrorDialog("Failed to read folder", err)
return
}
if reader == nil {
return
}
u.loadDocument(reader)
}, u.window)
d.Show()
filterEnabled := u.app.Preferences().BoolWithFallback(settingExtensionFilter, settingExtensionDefault)
if filterEnabled {
f := storage.NewExtensionFileFilter([]string{".json"})
d.SetFilter(f)
}
}

// fileNew resets the app to it's initial state
func (u *UI) fileNew() {
u.document.Reset()
u.setTitle("")
u.statusBar.reset()
u.welcomeMessage.Show()
u.toogleHasDocument(false)
u.selection.reset()
u.value.reset()
}

func (u *UI) fileReload() {
if u.currentFile == nil {
return
}
reader, err := storage.Reader(u.currentFile)
if err != nil {
u.showErrorDialog("Failed to reload file", err)
return
}
u.loadDocument(reader)
}

func (u *UI) extractSelection() ([]byte, error) {
uid := u.selection.selectedUID
n := u.document.Value(uid)
Expand Down Expand Up @@ -224,3 +266,15 @@ func (u *UI) toogleViewDetail() {
menuItem.Checked = u.value.isShown()
u.viewMenu.Refresh()
}

// addShortcutFromMenuItem is a helper for defining shortcuts.
// It allows to add an already defined shortcut from a menu item to the canvas.
//
// For example:
//
// window.Canvas().AddShortcut(menuItem)
func addShortcutFromMenuItem(item *fyne.MenuItem) (fyne.Shortcut, func(fyne.Shortcut)) {
return item.Shortcut, func(s fyne.Shortcut) {
item.Action()
}
}
22 changes: 11 additions & 11 deletions internal/ui/searchbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var type2importance = map[jsondocument.JSONType]widget.Importance{
// searchBarFrame represents the search bar frame in the UI.
type searchBarFrame struct {
content *fyne.Container
ui *UI
u *UI

searchEntry *widget.Entry
searchButton *ttwidget.Button
Expand All @@ -48,7 +48,7 @@ type searchBarFrame struct {

func (u *UI) newSearchBarFrame() *searchBarFrame {
f := &searchBarFrame{
ui: u,
u: u,
searchEntry: widget.NewEntry(),
}
// search frame
Expand All @@ -71,15 +71,15 @@ func (u *UI) newSearchBarFrame() *searchBarFrame {
})
f.searchButton.SetToolTip("Search")
f.scrollBottom = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceVerticalalignbottomSvg), func() {
f.ui.treeWidget.ScrollToBottom()
f.u.treeWidget.ScrollToBottom()
})
f.scrollBottom.SetToolTip("Scroll to bottom")
f.scrollTop = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceVerticalaligntopSvg), func() {
f.ui.treeWidget.ScrollToTop()
f.u.treeWidget.ScrollToTop()
})
f.scrollTop.SetToolTip("Scroll to top")
f.collapseAll = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceUnfoldlessSvg), func() {
f.ui.treeWidget.CloseAllBranches()
f.u.treeWidget.CloseAllBranches()
})
f.collapseAll.SetToolTip("Collapse all")
c := container.NewBorder(
Expand Down Expand Up @@ -131,7 +131,7 @@ func (f *searchBarFrame) doSearch() {
b := widget.NewButton("Cancel", func() {
cancel()
})
d := dialog.NewCustomWithoutButtons("Search", container.NewVBox(c, b), f.ui.window)
d := dialog.NewCustomWithoutButtons("Search", container.NewVBox(c, b), f.u.window)
d.Show()
d.SetOnClosed(func() {
cancel()
Expand All @@ -146,30 +146,30 @@ func (f *searchBarFrame) doSearch() {
search = strings.ToLower(search)
if search != "true" && search != "false" && search != "null" {
d.Hide()
f.ui.showErrorDialog("Allowed keywords are: true, false, null", nil)
f.u.showErrorDialog("Allowed keywords are: true, false, null", nil)
return
}
case searchTypeString:
typ = jsondocument.SearchString
case searchTypeNumber:
typ = jsondocument.SearchNumber
}
uid, err := f.ui.document.Search(ctx, f.ui.selection.selectedUID, search, typ)
uid, err := f.u.document.Search(ctx, f.u.selection.selectedUID, search, typ)
d.Hide()
if errors.Is(err, jsondocument.ErrCallerCanceled) {
return
} else if errors.Is(err, jsondocument.ErrNotFound) {
d2 := dialog.NewInformation(
"No match",
fmt.Sprintf("No %s found matching %s", searchType, search),
f.ui.window,
f.u.window,
)
d2.Show()
return
} else if err != nil {
f.ui.showErrorDialog("Search failed", err)
f.u.showErrorDialog("Search failed", err)
return
}
f.ui.scrollTo(uid)
f.u.scrollTo(uid)
}()
}
14 changes: 7 additions & 7 deletions internal/ui/selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
// selection represents the selection frame in the UI.
type selectionFrame struct {
content *fyne.Container
ui *UI
u *UI

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

f := &selectionFrame{
ui: u,
u: u,
selectedPath: container.New(myHBox),
}
f.jumpToSelection = ttwidget.NewButtonWithIcon("", theme.NewThemedResource(resourceReadmoreSvg), func() {
Expand Down Expand Up @@ -88,19 +88,19 @@ type NodePlus struct {

func (f *selectionFrame) set(uid string) {
f.selectedUID = uid
p := f.ui.document.Path(uid)
p := f.u.document.Path(uid)
var path []NodePlus
for _, uid2 := range p {
path = append(path, NodePlus{Node: f.ui.document.Value(uid2), UID: uid2})
path = append(path, NodePlus{Node: f.u.document.Value(uid2), UID: uid2})
}
path = append(path, NodePlus{Node: f.ui.document.Value(uid), UID: uid})
path = append(path, NodePlus{Node: f.u.document.Value(uid), UID: uid})
f.selectedPath.RemoveAll()
for i, n := range path {
isLast := i == len(path)-1
if !isLast {
l := kxwidget.NewTappableLabel(n.Key, func() {
f.ui.scrollTo(n.UID)
f.ui.selectElement(n.UID)
f.u.scrollTo(n.UID)
f.u.selectElement(n.UID)
})
f.selectedPath.Add(l)
} else {
Expand Down
Loading

0 comments on commit 4c27233

Please sign in to comment.