Skip to content

Commit

Permalink
internal/ui: move control server changing to its own dialog (#139)
Browse files Browse the repository at this point in the history
* internal/ui: add `Change Control Server` no-op menu item

* internal/ui: remove control server setting from the preferences dialog and the dconf entry

* internal/ui: open a prompt for the control server URL

`Prompt` isn't particularly customizable, huh... I'll have to do
something about that.

* internal/ui: rework `Prompt` to be siginficantly more configurable

* internal/ui: implement control server changing

* meta: add v0.13.0 to metainfo
  • Loading branch information
DeedleFake authored Jul 12, 2024
1 parent 796ce73 commit 79b5782
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 84 deletions.
8 changes: 0 additions & 8 deletions dev.deedles.Trayscale.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@
for status changes and other things.
</description>
</key>
<key name="control-plane-server" type="s">
<default>""</default>
<summary>Control plane server to use</summary>
<description>
If not empty, uses the specified control plane server instead
of the default Tailscale one.
</description>
</key>
</schema>
</schemalist>

Expand Down
5 changes: 5 additions & 0 deletions dev.deedles.Trayscale.metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
<content_rating type="oars-1.1" />

<releases>
<release version="v0.13.0" date="TODO">
<description>
<ul>Remove control server dconf setting and instead use a new dialog.</ul>
</description>
</release>
<release version="v0.12.7" date="2024-07-09">
<description>
<ul>Add a connection toggle to the tray icon menu.</ul>
Expand Down
36 changes: 4 additions & 32 deletions internal/ui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import (
"deedles.dev/mk"
"deedles.dev/trayscale/internal/tray"
"deedles.dev/trayscale/internal/tsutil"
"deedles.dev/trayscale/internal/version"
"github.com/diamondburned/gotk4-adwaita/pkg/adw"
"github.com/diamondburned/gotk4/pkg/gdk/v4"
"github.com/diamondburned/gotk4/pkg/gio/v2"
"github.com/diamondburned/gotk4/pkg/glib/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v4"
"tailscale.com/ipn"
"tailscale.com/types/key"
)

Expand Down Expand Up @@ -44,26 +42,6 @@ type App struct {
operatorCheck bool
}

// showAbout shows the app's about dialog.
func (a *App) showAbout() {
dialog := adw.NewAboutWindow()
dialog.SetDevelopers([]string{"DeedleFake"})
dialog.SetCopyright("Copyright (c) 2023 DeedleFake")
dialog.SetLicense(readAssetString("LICENSE"))
dialog.SetLicenseType(gtk.LicenseCustom)
dialog.SetApplicationIcon(appID)
dialog.SetApplicationName("Trayscale")
dialog.SetWebsite("https://github.com/DeedleFake/trayscale")
dialog.SetIssueURL("https://github.com/DeedleFake/trayscale/issues")
if v, ok := version.Get(); ok {
dialog.SetVersion(v)
}
dialog.SetTransientFor(&a.win.Window)
dialog.Show()

a.app.AddWindow(&dialog.Window.Window)
}

func (a *App) clip(v *glib.Value) {
gdk.DisplayGetDefault().Clipboard().Set(v)
}
Expand Down Expand Up @@ -197,16 +175,6 @@ func (a *App) update(s tsutil.Status) {
a.win.StatusSwitch.SetActive(online)
a.updatePeers(s)

if a.settings != nil {
controlURL := a.settings.String("control-plane-server")
if controlURL == "" {
controlURL = ipn.DefaultControlURL
}
if controlURL != s.Prefs.ControlURL {
a.settings.SetString("control-plane-server", s.Prefs.ControlURL)
}
}

if a.online && !a.operatorCheck {
a.operatorCheck = true
if !s.OperatorIsCurrent() {
Expand Down Expand Up @@ -286,6 +254,10 @@ func (a *App) onAppActivate(ctx context.Context) {
return
}

changeControlServerAction := gio.NewSimpleAction("change_control_server", nil)
changeControlServerAction.ConnectActivate(func(p *glib.Variant) { a.showChangeControlServer() })
a.app.AddAction(changeControlServerAction)

preferencesAction := gio.NewSimpleAction("preferences", nil)
preferencesAction.ConnectActivate(func(p *glib.Variant) { a.showPreferences() })
a.app.AddAction(preferencesAction)
Expand Down
39 changes: 26 additions & 13 deletions internal/ui/dialogs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,43 @@ func (d Confirmation) Show(a *App, res func(bool)) {
}

type Prompt struct {
Heading string
Body string
Heading string
Body string
Responses []PromptResponse
}

func (d Prompt) Show(a *App, res func(val string)) {
type PromptResponse struct {
ID string
Label string
Appearance adw.ResponseAppearance
Default bool
}

func (d Prompt) Show(a *App, initialValue string, res func(response, val string)) {
input := gtk.NewText()
if initialValue != "" {
input.Buffer().SetText(initialValue, len(initialValue))
}

dialog := adw.NewMessageDialog(&a.win.Window, d.Heading, d.Body)
dialog.SetExtraChild(input)
dialog.AddResponse("cancel", "_Cancel")
dialog.SetCloseResponse("cancel")
dialog.AddResponse("add", "_Add")
dialog.SetResponseAppearance("add", adw.ResponseSuggested)
dialog.SetDefaultResponse("add")

dialog.ConnectResponse(func(response string) {
switch response {
case "add":
res(input.Buffer().Text())
def := "activate"
for _, r := range d.Responses {
dialog.AddResponse(r.ID, r.Label)
dialog.SetResponseAppearance(r.ID, r.Appearance)
if r.Default {
dialog.SetDefaultResponse(r.ID)
def = r.ID
}
}

dialog.ConnectResponse(func(response string) {
res(response, input.Buffer().Text())
})
input.ConnectActivate(func() {
defer dialog.Close()
res(input.Buffer().Text())
res(def, input.Buffer().Text())
})

dialog.Show()
Expand Down
4 changes: 4 additions & 0 deletions internal/ui/menu.ui
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
<requires lib="libadwaita" version="1.1"/>
<menu id="MainMenu">
<section>
<item>
<attribute name="label">Change Control _Server</attribute>
<attribute name="action">app.change_control_server</attribute>
</item>
<item>
<attribute name="label">_Preferences</attribute>
<attribute name="action">app.preferences</attribute>
Expand Down
1 change: 0 additions & 1 deletion internal/ui/preferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ type PreferencesWindow struct {
UseTrayIconRow *adw.SwitchRow
PollingIntervalRow *adw.SpinRow
PollingIntervalAdjustment *gtk.Adjustment
ControlURLRow *adw.EntryRow
}

func NewPreferencesWindow() *PreferencesWindow {
Expand Down
11 changes: 0 additions & 11 deletions internal/ui/preferences.ui
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,6 @@
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title">Advanced</property>
<child>
<object class="AdwEntryRow" id="ControlURLRow">
<property name="show-apply-button">True</property>
<property name="title">Control Server URL</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
Expand Down
13 changes: 12 additions & 1 deletion internal/ui/selfpage.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,18 @@ func (page *SelfPage) init(a *App, peer *ipnstate.PeerStatus, status tsutil.Stat
})

page.AdvertiseRouteButton.ConnectClicked(func() {
Prompt{"Add IP", "IP prefix to advertise"}.Show(a, func(val string) {
Prompt{
Heading: "Add IP",
Body: "IP prefix to advertise",
Responses: []PromptResponse{
{ID: "cancel", Label: "_Cancel"},
{ID: "add", Label: "_Add", Appearance: adw.ResponseSuggested, Default: true},
},
}.Show(a, "", func(response, val string) {
if response != "add" {
return
}

p, err := netip.ParsePrefix(val)
if err != nil {
slog.Error("parse prefix", "err", err)
Expand Down
68 changes: 55 additions & 13 deletions internal/ui/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ package ui

import (
"context"
"fmt"
"log/slog"
"slices"
"time"

"deedles.dev/trayscale/internal/tray"
"deedles.dev/trayscale/internal/tsutil"
"deedles.dev/trayscale/internal/version"
"github.com/diamondburned/gotk4-adwaita/pkg/adw"
"github.com/diamondburned/gotk4/pkg/gio/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v4"
"tailscale.com/ipn"
)

func (a *App) initSettings(ctx context.Context) {
Expand All @@ -29,15 +34,6 @@ func (a *App) initSettings(ctx context.Context) {

case "polling-interval":
a.poller.SetInterval() <- a.getInterval()

case "control-plane-server":
url := a.settings.String("control-plane-server")
err := tsutil.SetControlURL(ctx, url)
if err != nil {
slog.Error("update control plane server URL", "err", err, "url", url)
return
}
a.poller.Poll() <- struct{}{}
}
})

Expand All @@ -47,6 +43,36 @@ init:
}
}

func (a *App) showChangeControlServer() {
status := <-a.poller.Get()

Prompt{
Heading: "Control Server URL",
Responses: []PromptResponse{
{ID: "cancel", Label: "_Cancel"},
{ID: "default", Label: "Use _Default"},
{ID: "set", Label: "_Set URL", Appearance: adw.ResponseSuggested, Default: true},
},
}.Show(a, status.Prefs.ControlURL, func(response, val string) {
switch response {
case "default":
val = ipn.DefaultControlURL
fallthrough // Oh my.
case "set":
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

err := tsutil.SetControlURL(ctx, val)
if err != nil {
slog.Error("update control plane server URL", "err", err, "url", val)
a.toast(fmt.Sprintf("Error setting control URL: %v", err))
return
}
a.poller.Poll() <- struct{}{}
}
})
}

func (a *App) showPreferences() {
if a.settings == nil {
a.toast("Settings schema not found")
Expand All @@ -56,16 +82,32 @@ func (a *App) showPreferences() {
win := NewPreferencesWindow()
a.settings.Bind("tray-icon", win.UseTrayIconRow.Object, "active", gio.SettingsBindDefault)
a.settings.Bind("polling-interval", win.PollingIntervalAdjustment.Object, "value", gio.SettingsBindDefault)
a.settings.Bind("control-plane-server", win.ControlURLRow.Object, "text", gio.SettingsBindGet)
win.ControlURLRow.ConnectApply(func() {
a.settings.SetString("control-plane-server", win.ControlURLRow.Text())
})
win.SetTransientFor(&a.win.Window)
win.Show()

a.app.AddWindow(&win.Window.Window)
}

// showAbout shows the app's about dialog.
func (a *App) showAbout() {
dialog := adw.NewAboutWindow()
dialog.SetDevelopers([]string{"DeedleFake"})
dialog.SetCopyright("Copyright (c) 2023 DeedleFake")
dialog.SetLicense(readAssetString("LICENSE"))
dialog.SetLicenseType(gtk.LicenseCustom)
dialog.SetApplicationIcon(appID)
dialog.SetApplicationName("Trayscale")
dialog.SetWebsite("https://github.com/DeedleFake/trayscale")
dialog.SetIssueURL("https://github.com/DeedleFake/trayscale/issues")
if v, ok := version.Get(); ok {
dialog.SetVersion(v)
}
dialog.SetTransientFor(&a.win.Window)
dialog.Show()

a.app.AddWindow(&dialog.Window.Window)
}

func (a *App) getInterval() time.Duration {
if a.settings == nil {
return 5 * time.Second
Expand Down
5 changes: 0 additions & 5 deletions internal/ui/trayscale.cmb
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@
(3,1,"AdwPreferencesWindow","PreferencesWindow",None,None,None,None,None,None,None),
(3,2,"AdwPreferencesPage",None,1,None,None,None,None,None,None),
(3,3,"AdwPreferencesGroup",None,2,None,None,None,None,None,None),
(3,10,"AdwPreferencesGroup",None,2,None,None,None,1,None,None),
(3,11,"AdwEntryRow","ControlURLRow",10,None,None,None,None,None,None),
(3,14,"AdwSwitchRow","UseTrayIconRow",3,None,None,None,-1,None,None),
(3,15,"AdwSpinRow","PollingIntervalRow",3,None,None,None,2,None,None),
(3,16,"GtkAdjustment","PollingIntervalAdjustment",15,None,None,None,None,None,None),
Expand Down Expand Up @@ -148,9 +146,6 @@
(2,149,"AdwPreferencesRow","title","Bytes received",None,None,None,None,None,None,None,None,None),
(2,151,"AdwPreferencesRow","title","Bytes sent",None,None,None,None,None,None,None,None,None),
(3,3,"AdwPreferencesGroup","title","General",None,None,None,None,None,None,None,None,None),
(3,10,"AdwPreferencesGroup","title","Advanced",None,None,None,None,None,None,None,None,None),
(3,11,"AdwEntryRow","show-apply-button","True",None,None,None,None,None,None,None,None,None),
(3,11,"AdwPreferencesRow","title","Control Server URL",None,None,None,None,None,None,None,None,None),
(3,14,"AdwActionRow","subtitle","If enabled, an icon will be added to the system tray",None,None,None,None,None,None,None,None,None),
(3,14,"AdwPreferencesRow","title","Use Tray Icon",None,None,None,None,None,None,None,None,None),
(3,15,"AdwActionRow","subtitle","Interval, in seconds, at which to poll the Tailscale daemon",None,None,None,None,None,None,None,None,None),
Expand Down

0 comments on commit 79b5782

Please sign in to comment.