diff --git a/dev.deedles.Trayscale.metainfo.xml b/dev.deedles.Trayscale.metainfo.xml index 7169e75..fd56257 100644 --- a/dev.deedles.Trayscale.metainfo.xml +++ b/dev.deedles.Trayscale.metainfo.xml @@ -54,6 +54,12 @@ + + +
    Update to use new Libadwaita widgets.
+
    Fix a bug with ordering of Mullvad nodes.
+
+
    Fix self and Mullvad pages disappearing when re-opening the window.
diff --git a/internal/tsutil/tsutil.go b/internal/tsutil/tsutil.go index 8b37352..0398a87 100644 --- a/internal/tsutil/tsutil.go +++ b/internal/tsutil/tsutil.go @@ -50,3 +50,16 @@ func CompareLocations(loc1, loc2 *tailcfg.Location) int { cmp.Compare(loc1.City, loc2.City), ) } + +// ComparePeers compares two peers. It does so by location if +// available or hostname if not. It returns the peers in a +// deterministic order if their locations or hostnames are identical, +// so the result of calling this is never 0. To determine if peers are +// the same, compare their IDs manually. +func ComparePeers(p1, p2 *ipnstate.PeerStatus) int { + ids := cmp.Compare(p1.ID, p2.ID) + if (p1.Location == nil) || (p2.Location == nil) { + return cmp.Or(cmp.Compare(p1.HostName, p2.HostName), ids) + } + return cmp.Or(CompareLocations(p1.Location, p2.Location), ids) +} diff --git a/internal/ui/mainwindow.ui b/internal/ui/mainwindow.ui index ed28761..ef43442 100644 --- a/internal/ui/mainwindow.ui +++ b/internal/ui/mainwindow.ui @@ -1,5 +1,5 @@ - + @@ -13,7 +13,7 @@ 200 - + max-width: 400sp True @@ -67,7 +67,7 @@ open-menu-symbolic True - + MainMenu diff --git a/internal/ui/mullvadpage.go b/internal/ui/mullvadpage.go index c7463a3..2b87abc 100644 --- a/internal/ui/mullvadpage.go +++ b/internal/ui/mullvadpage.go @@ -1,7 +1,6 @@ package ui import ( - "cmp" "context" _ "embed" "fmt" @@ -55,17 +54,15 @@ func (page *MullvadPage) init(a *App, status tsutil.Status) { row := exitNodeRow{ peer: peer, - w: adw.NewActionRow(), - r: gtk.NewSwitch(), + w: adw.NewSwitchRow(), } - row.w.AddSuffix(row.r) row.w.SetTitle(mullvadExitNodeName(peer)) - row.r.SetMarginTop(12) - row.r.SetMarginBottom(12) - row.r.ConnectStateSet(func(s bool) bool { - if s == row.r.State() { + row.r().SetMarginTop(12) + row.r().SetMarginBottom(12) + row.r().ConnectStateSet(func(s bool) bool { + if s == row.r().State() { return false } @@ -84,7 +81,7 @@ func (page *MullvadPage) init(a *App, status tsutil.Status) { err := a.TS.ExitNode(context.TODO(), node) if err != nil { slog.Error("set exit node", "err", err) - row.r.SetActive(!s) + row.r().SetActive(!s) return true } a.poller.Poll() <- struct{}{} @@ -112,12 +109,7 @@ func (page *MullvadPage) Update(a *App, peer *ipnstate.PeerStatus, status tsutil } } } - slices.SortFunc(nodes, func(p1, p2 *ipnstate.PeerStatus) int { - if (p1.Location == nil) || (p2.Location == nil) { - return cmp.Compare(p1.HostName, p2.HostName) - } - return tsutil.CompareLocations(p1.Location, p2.Location) - }) + slices.SortFunc(nodes, tsutil.ComparePeers) page.exitNodeRows.Update(nodes) } @@ -125,8 +117,11 @@ func (page *MullvadPage) Update(a *App, peer *ipnstate.PeerStatus, status tsutil type exitNodeRow struct { peer *ipnstate.PeerStatus - w *adw.ActionRow - r *gtk.Switch + w *adw.SwitchRow +} + +func (row *exitNodeRow) r() *gtk.Switch { + return row.w.ActivatableWidget().(*gtk.Switch) } func (row *exitNodeRow) Update(peer *ipnstate.PeerStatus) { @@ -134,8 +129,8 @@ func (row *exitNodeRow) Update(peer *ipnstate.PeerStatus) { row.w.SetTitle(mullvadExitNodeName(peer)) - row.r.SetState(peer.ExitNode) - row.r.SetActive(peer.ExitNode) + row.r().SetState(peer.ExitNode) + row.r().SetActive(peer.ExitNode) } func (row *exitNodeRow) Widget() gtk.Widgetter { diff --git a/internal/ui/mullvadpage.ui b/internal/ui/mullvadpage.ui index a0fb5ce..bf85f9b 100644 --- a/internal/ui/mullvadpage.ui +++ b/internal/ui/mullvadpage.ui @@ -1,5 +1,5 @@ - + diff --git a/internal/ui/peerpage.go b/internal/ui/peerpage.go index cde941c..9d47ce8 100644 --- a/internal/ui/peerpage.go +++ b/internal/ui/peerpage.go @@ -46,8 +46,7 @@ type PeerPage struct { PreferredDERP *gtk.Label DERPLatencies *adw.ExpanderRow MiscGroup *adw.PreferencesGroup - ExitNodeRow *adw.ActionRow - ExitNodeSwitch *gtk.Switch + ExitNodeRow *adw.SwitchRow OnlineRow *adw.ActionRow Online *gtk.Image LastSeenRow *adw.ActionRow @@ -186,8 +185,8 @@ func (page *PeerPage) init(a *App, peer *ipnstate.PeerStatus, status tsutil.Stat return &row } - page.ExitNodeSwitch.ConnectStateSet(func(s bool) bool { - if s == page.ExitNodeSwitch.State() { + page.ExitNodeRow.ActivatableWidget().(*gtk.Switch).ConnectStateSet(func(s bool) bool { + if s == page.ExitNodeRow.ActivatableWidget().(*gtk.Switch).State() { return false } @@ -206,7 +205,7 @@ func (page *PeerPage) init(a *App, peer *ipnstate.PeerStatus, status tsutil.Stat err := a.TS.ExitNode(context.TODO(), node) if err != nil { slog.Error("set exit node", "err", err) - page.ExitNodeSwitch.SetActive(!s) + page.ExitNodeRow.ActivatableWidget().(*gtk.Switch).SetActive(!s) return true } a.poller.Poll() <- struct{}{} @@ -242,8 +241,8 @@ func (page *PeerPage) Update(a *App, peer *ipnstate.PeerStatus, status tsutil.St page.routeRows.Update(eroutes) page.ExitNodeRow.SetVisible(peer.ExitNodeOption) - page.ExitNodeSwitch.SetState(peer.ExitNode) - page.ExitNodeSwitch.SetActive(peer.ExitNode) + page.ExitNodeRow.ActivatableWidget().(*gtk.Switch).SetState(peer.ExitNode) + page.ExitNodeRow.ActivatableWidget().(*gtk.Switch).SetActive(peer.ExitNode) page.RxBytes.SetText(strconv.FormatInt(peer.RxBytes, 10)) page.TxBytes.SetText(strconv.FormatInt(peer.TxBytes, 10)) page.Created.SetText(formatTime(peer.Created)) diff --git a/internal/ui/peerpage.ui b/internal/ui/peerpage.ui index 2420937..7c9779a 100644 --- a/internal/ui/peerpage.ui +++ b/internal/ui/peerpage.ui @@ -1,8 +1,8 @@ - + - + @@ -45,15 +45,8 @@ Misc. - - ExitNodeSwitch + Use as exit node - - - 12 - 12 - - diff --git a/internal/ui/preferences.go b/internal/ui/preferences.go index 0df364a..7956f4e 100644 --- a/internal/ui/preferences.go +++ b/internal/ui/preferences.go @@ -13,10 +13,8 @@ var preferencesXML string type PreferencesWindow struct { *adw.PreferencesWindow `gtk:"PreferencesWindow"` - UseTrayIconRow *adw.ActionRow - UseTrayIcon *gtk.Switch - PollingIntervalRow *adw.ActionRow - PollingInterval *gtk.SpinButton + UseTrayIconRow *adw.SwitchRow + PollingIntervalRow *adw.SpinRow PollingIntervalAdjustment *gtk.Adjustment ControlURLRow *adw.EntryRow } diff --git a/internal/ui/preferences.ui b/internal/ui/preferences.ui index 6af7a92..c550318 100644 --- a/internal/ui/preferences.ui +++ b/internal/ui/preferences.ui @@ -1,8 +1,8 @@ - + - + @@ -10,28 +10,23 @@ General - + If enabled, an icon will be added to the system tray Use Tray Icon - - - 12 - 12 - - - + + + + 0.5 + 1.0 + 100.0 + 5.0 + + Interval, in seconds, at which to poll the Tailscale daemon Polling Interval - - - PollingIntervalAdjustment - 12 - 12 - - @@ -50,10 +45,4 @@ - - 0.5 - 1.0 - 100.0 - 5.0 - diff --git a/internal/ui/selfpage.go b/internal/ui/selfpage.go index 7a8b3df..fd5b4cf 100644 --- a/internal/ui/selfpage.go +++ b/internal/ui/selfpage.go @@ -27,40 +27,37 @@ var selfPageXML string type SelfPage struct { *adw.StatusPage `gtk:"Page"` - IPGroup *adw.PreferencesGroup - OptionsGroup *adw.PreferencesGroup - AdvertiseExitNodeRow *adw.ActionRow - AdvertiseExitNodeSwitch *gtk.Switch - AllowLANAccessRow *adw.ActionRow - AllowLANAccessSwitch *gtk.Switch - AcceptRoutesRow *adw.ActionRow - AcceptRoutesSwitch *gtk.Switch - AdvertisedRoutesGroup *adw.PreferencesGroup - AdvertiseRouteButton *gtk.Button - NetCheckGroup *adw.PreferencesGroup - NetCheckButton *gtk.Button - LastNetCheckRow *adw.ActionRow - LastNetCheck *gtk.Label - UDPRow *adw.ActionRow - UDP *gtk.Image - IPv4Row *adw.ActionRow - IPv4Icon *gtk.Image - IPv4Addr *gtk.Label - IPv6Row *adw.ActionRow - IPv6Icon *gtk.Image - IPv6Addr *gtk.Label - UPnPRow *adw.ActionRow - UPnP *gtk.Image - PMPRow *adw.ActionRow - PMP *gtk.Image - PCPRow *adw.ActionRow - PCP *gtk.Image - HairPinningRow *adw.ActionRow - HairPinning *gtk.Image - PreferredDERPRow *adw.ActionRow - PreferredDERP *gtk.Label - DERPLatencies *adw.ExpanderRow - FilesGroup *adw.PreferencesGroup + IPGroup *adw.PreferencesGroup + OptionsGroup *adw.PreferencesGroup + AdvertiseExitNodeRow *adw.SwitchRow + AllowLANAccessRow *adw.SwitchRow + AcceptRoutesRow *adw.SwitchRow + AdvertisedRoutesGroup *adw.PreferencesGroup + AdvertiseRouteButton *gtk.Button + NetCheckGroup *adw.PreferencesGroup + NetCheckButton *gtk.Button + LastNetCheckRow *adw.ActionRow + LastNetCheck *gtk.Label + UDPRow *adw.ActionRow + UDP *gtk.Image + IPv4Row *adw.ActionRow + IPv4Icon *gtk.Image + IPv4Addr *gtk.Label + IPv6Row *adw.ActionRow + IPv6Icon *gtk.Image + IPv6Addr *gtk.Label + UPnPRow *adw.ActionRow + UPnP *gtk.Image + PMPRow *adw.ActionRow + PMP *gtk.Image + PCPRow *adw.ActionRow + PCP *gtk.Image + HairPinningRow *adw.ActionRow + HairPinning *gtk.Image + PreferredDERPRow *adw.ActionRow + PreferredDERP *gtk.Label + DERPLatencies *adw.ExpanderRow + FilesGroup *adw.PreferencesGroup peer *ipnstate.PeerStatus name string @@ -229,8 +226,8 @@ func (page *SelfPage) init(a *App, peer *ipnstate.PeerStatus, status tsutil.Stat return &row } - page.AdvertiseExitNodeSwitch.ConnectStateSet(func(s bool) bool { - if s == page.AdvertiseExitNodeSwitch.State() { + page.AdvertiseExitNodeRow.ActivatableWidget().(*gtk.Switch).ConnectStateSet(func(s bool) bool { + if s == page.AdvertiseExitNodeRow.ActivatableWidget().(*gtk.Switch).State() { return false } @@ -245,37 +242,37 @@ func (page *SelfPage) init(a *App, peer *ipnstate.PeerStatus, status tsutil.Stat err := a.TS.AdvertiseExitNode(context.TODO(), s) if err != nil { slog.Error("advertise exit node", "err", err) - page.AdvertiseExitNodeSwitch.SetActive(!s) + page.AdvertiseExitNodeRow.ActivatableWidget().(*gtk.Switch).SetActive(!s) return true } a.poller.Poll() <- struct{}{} return true }) - page.AllowLANAccessSwitch.ConnectStateSet(func(s bool) bool { - if s == page.AllowLANAccessSwitch.State() { + page.AllowLANAccessRow.ActivatableWidget().(*gtk.Switch).ConnectStateSet(func(s bool) bool { + if s == page.AllowLANAccessRow.ActivatableWidget().(*gtk.Switch).State() { return false } err := a.TS.AllowLANAccess(context.TODO(), s) if err != nil { slog.Error("allow LAN access", "err", err) - page.AllowLANAccessSwitch.SetActive(!s) + page.AllowLANAccessRow.ActivatableWidget().(*gtk.Switch).SetActive(!s) return true } a.poller.Poll() <- struct{}{} return true }) - page.AcceptRoutesSwitch.ConnectStateSet(func(s bool) bool { - if s == page.AcceptRoutesSwitch.State() { + page.AcceptRoutesRow.ActivatableWidget().(*gtk.Switch).ConnectStateSet(func(s bool) bool { + if s == page.AcceptRoutesRow.ActivatableWidget().(*gtk.Switch).State() { return false } err := a.TS.AcceptRoutes(context.TODO(), s) if err != nil { slog.Error("accept routes", "err", err) - page.AcceptRoutesSwitch.SetActive(!s) + page.AcceptRoutesRow.ActivatableWidget().(*gtk.Switch).SetActive(!s) return true } a.poller.Poll() <- struct{}{} @@ -384,12 +381,12 @@ func (page *SelfPage) Update(a *App, peer *ipnstate.PeerStatus, status tsutil.St slices.SortFunc(peer.TailscaleIPs, netip.Addr.Compare) page.addrRows.Update(peer.TailscaleIPs) - page.AdvertiseExitNodeSwitch.SetState(status.Prefs.AdvertisesExitNode()) - page.AdvertiseExitNodeSwitch.SetActive(status.Prefs.AdvertisesExitNode()) - page.AllowLANAccessSwitch.SetState(status.Prefs.ExitNodeAllowLANAccess) - page.AllowLANAccessSwitch.SetActive(status.Prefs.ExitNodeAllowLANAccess) - page.AcceptRoutesSwitch.SetState(status.Prefs.RouteAll) - page.AcceptRoutesSwitch.SetActive(status.Prefs.RouteAll) + page.AdvertiseExitNodeRow.ActivatableWidget().(*gtk.Switch).SetState(status.Prefs.AdvertisesExitNode()) + page.AdvertiseExitNodeRow.ActivatableWidget().(*gtk.Switch).SetActive(status.Prefs.AdvertisesExitNode()) + page.AllowLANAccessRow.ActivatableWidget().(*gtk.Switch).SetState(status.Prefs.ExitNodeAllowLANAccess) + page.AllowLANAccessRow.ActivatableWidget().(*gtk.Switch).SetActive(status.Prefs.ExitNodeAllowLANAccess) + page.AcceptRoutesRow.ActivatableWidget().(*gtk.Switch).SetState(status.Prefs.RouteAll) + page.AcceptRoutesRow.ActivatableWidget().(*gtk.Switch).SetActive(status.Prefs.RouteAll) page.fileRows.Update(status.Files) page.FilesGroup.SetVisible(len(status.Files) > 0) diff --git a/internal/ui/selfpage.ui b/internal/ui/selfpage.ui index b0fd129..33fa4d9 100644 --- a/internal/ui/selfpage.ui +++ b/internal/ui/selfpage.ui @@ -1,8 +1,8 @@ - + - + @@ -19,39 +19,18 @@ Options - - AdvertiseExitNodeSwitch + Advertise exit node - - - 12 - 12 - - - - AllowLANAccessSwitch + Allow LAN access - - - 12 - 12 - - - - AcceptRoutesSwitch + Accept routes - - - 12 - 12 - - diff --git a/internal/ui/settings.go b/internal/ui/settings.go index 2482710..cb3540d 100644 --- a/internal/ui/settings.go +++ b/internal/ui/settings.go @@ -53,7 +53,7 @@ func (a *App) showPreferences() { } win := NewPreferencesWindow() - a.settings.Bind("tray-icon", win.UseTrayIcon.Object, "active", gio.SettingsBindDefault) + 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() { diff --git a/internal/ui/trayscale.cmb b/internal/ui/trayscale.cmb index 985b7b5..3c227a8 100644 --- a/internal/ui/trayscale.cmb +++ b/internal/ui/trayscale.cmb @@ -1,6 +1,6 @@ - + (1,None,"mainwindow.ui","mainwindow.ui",None,None,None,None,None,None,None), (2,None,None,"peerpage.ui",None,None,None,None,None,None,None), @@ -9,98 +9,92 @@ (5,None,None,"mullvadpage.ui",None,None,None,None,None,None,None) - (1,1,"AdwApplicationWindow","MainWindow",None,None,None,None,None,None), - (1,4,"AdwToastOverlay","ToastOverlay",1,None,None,None,2,None), - (1,21,"AdwNavigationSplitView","SplitView",4,None,None,None,-1,None), - (1,34,"AdwNavigationPage",None,21,None,None,None,-1,None), - (1,35,"AdwNavigationPage",None,21,None,None,None,-1,None), - (1,52,"AdwToolbarView",None,34,None,None,None,-1,None), - (1,57,"AdwHeaderBar",None,52,None,"top",None,1,None), - (1,58,"GtkSpinner","WorkSpinner",57,None,"end",None,None,None), - (1,60,"GtkStack","PeersStack",52,None,None,None,-1,None), - (1,61,"AdwToolbarView",None,35,None,None,None,-1,None), - (1,67,"AdwHeaderBar",None,61,None,"top",None,1,None), - (1,68,"GtkSwitch","StatusSwitch",67,None,"start",None,None,None), - (1,69,"GtkMenuButton","MainMenuButton",67,None,"end",None,1,"<property name=\"menu-model\">MainMenu</property>"), - (1,71,"GtkStackSidebar",None,61,None,None,None,-1,None), - (1,72,"AdwBreakpoint",None,1,None,None,None,1,"<condition>max-width: 400sp</condition><setter object=\"SplitView\" property=\"collapsed\">True</setter>"), - (2,3,"AdwStatusPage","Page",None,None,None,None,None,None), - (2,73,"AdwClamp",None,3,None,None,None,1,None), - (2,74,"GtkBox",None,73,None,None,None,None,None), - (2,75,"AdwPreferencesGroup","IPGroup",74,None,None,None,None,None), - (2,82,"AdwPreferencesGroup","SendFileGroup",74,None,None,None,3,None), - (2,83,"AdwActionRow","SendFileRow",82,None,None,None,None,None), - (2,110,"AdwPreferencesGroup","MiscGroup",74,None,None,None,6,None), - (2,111,"AdwActionRow","ExitNodeRow",110,None,None,None,None,None), - (2,112,"GtkSwitch","ExitNodeSwitch",111,None,None,None,None,None), - (2,113,"AdwActionRow","OnlineRow",110,None,None,None,1,None), - (2,114,"GtkImage","Online",113,None,None,None,None,None), - (2,115,"AdwActionRow","LastSeenRow",110,None,None,None,2,None), - (2,116,"GtkLabel","LastSeen",115,None,None,None,None,None), - (2,117,"AdwActionRow","CreatedRow",110,None,None,None,3,None), - (2,118,"GtkLabel","Created",117,None,None,None,None,None), - (2,119,"AdwActionRow","LastWriteRow",110,None,None,None,4,None), - (2,120,"GtkLabel","LastWrite",119,None,None,None,None,None), - (2,121,"AdwActionRow","LastHandshakeRow",110,None,None,None,5,None), - (2,122,"GtkLabel","LastHandshake",121,None,None,None,None,None), - (2,123,"AdwActionRow","RxBytesRow",110,None,None,None,6,None), - (2,124,"GtkLabel","RxBytes",123,None,None,None,None,None), - (2,125,"AdwActionRow","TxBytesRow",110,None,None,None,7,None), - (2,126,"GtkLabel","TxBytes",125,None,None,None,None,None), - (2,135,"GtkDropTarget","DropTarget",None,None,None,None,None,None), - (2,136,"GtkButton","SendFileButton",83,None,None,None,None,None), - (2,137,"AdwPreferencesGroup","AdvertisedRoutesGroup",74,None,None,None,3,None), - (3,1,"AdwPreferencesWindow","PreferencesWindow",None,None,None,None,None,None), - (3,2,"AdwPreferencesPage",None,1,None,None,None,None,None), - (3,3,"AdwPreferencesGroup",None,2,None,None,None,None,None), - (3,4,"AdwActionRow","UseTrayIconRow",3,None,None,None,None,None), - (3,5,"GtkSwitch","UseTrayIcon",4,None,None,None,None,None), - (3,6,"AdwActionRow","PollingIntervalRow",3,None,None,None,2,None), - (3,7,"GtkSpinButton","PollingInterval",6,None,None,None,None,None), - (3,8,"GtkAdjustment","PollingIntervalAdjustment",None,None,None,None,None,None), - (3,10,"AdwPreferencesGroup",None,2,None,None,None,1,None), - (3,11,"AdwEntryRow","ControlURLRow",10,None,None,None,None,None), - (4,1,"AdwStatusPage","Page",None,None,None,None,None,None), - (4,2,"AdwClamp",None,1,None,None,None,None,None), - (4,3,"GtkBox",None,2,None,None,None,None,None), - (4,4,"AdwPreferencesGroup","IPGroup",3,None,None,None,None,None), - (4,5,"AdwPreferencesGroup","OptionsGroup",3,None,None,None,1,None), - (4,6,"AdwActionRow","AdvertiseExitNodeRow",5,None,None,None,None,None), - (4,7,"GtkSwitch","AdvertiseExitNodeSwitch",6,None,None,None,None,None), - (4,8,"AdwActionRow","AllowLANAccessRow",5,None,None,None,1,None), - (4,9,"GtkSwitch","AllowLANAccessSwitch",8,None,None,None,None,None), - (4,10,"AdwActionRow","AcceptRoutesRow",5,None,None,None,2,None), - (4,11,"GtkSwitch","AcceptRoutesSwitch",10,None,None,None,None,None), - (4,12,"AdwPreferencesGroup","FilesGroup",3,None,None,None,2,None), - (4,16,"AdwPreferencesGroup","AdvertisedRoutesGroup",3,None,None,None,4,None), - (4,17,"GtkButton","AdvertiseRouteButton",16,None,None,None,None,None), - (4,18,"AdwPreferencesGroup","NetCheckGroup",3,None,None,None,5,None), - (4,19,"GtkButton","NetCheckButton",18,None,None,None,None,None), - (4,20,"AdwActionRow","LastNetCheckRow",18,None,None,None,None,None), - (4,21,"GtkLabel","LastNetCheck",20,None,None,None,None,None), - (4,22,"AdwActionRow","UDPRow",18,None,None,None,1,None), - (4,23,"GtkImage","UDP",22,None,None,None,None,None), - (4,24,"AdwActionRow","IPv4Row",18,None,None,None,2,None), - (4,25,"GtkImage","IPv4Icon",24,None,None,None,None,None), - (4,26,"GtkLabel","IPv4Addr",24,None,None,None,1,None), - (4,27,"AdwActionRow","IPv6Row",18,None,None,None,3,None), - (4,28,"GtkImage","IPv6Icon",27,None,None,None,None,None), - (4,29,"GtkLabel","IPv6Addr",27,None,None,None,1,None), - (4,30,"AdwActionRow","UPnPRow",18,None,None,None,4,None), - (4,31,"GtkImage","UPnP",30,None,None,None,None,None), - (4,32,"AdwActionRow","PMPRow",18,None,None,None,5,None), - (4,33,"GtkImage","PMP",32,None,None,None,None,None), - (4,34,"AdwActionRow","PCPRow",18,None,None,None,6,None), - (4,35,"GtkImage","PCP",34,None,None,None,None,None), - (4,36,"AdwActionRow","HairPinningRow",18,None,None,None,7,None), - (4,37,"GtkImage","HairPinning",36,None,None,None,None,None), - (4,38,"AdwActionRow","PreferredDERPRow",18,None,None,None,8,None), - (4,39,"GtkLabel","PreferredDERP",38,None,None,None,None,None), - (4,40,"AdwExpanderRow","DERPLatencies",18,None,None,None,9,None), - (5,1,"AdwStatusPage","Page",None,None,None,None,None,None), - (5,2,"AdwClamp",None,1,None,None,None,None,None), - (5,3,"GtkBox",None,2,None,None,None,None,None), - (5,4,"AdwPreferencesGroup","ExitNodesGroup",3,None,None,None,None,None) + (1,1,"AdwApplicationWindow","MainWindow",None,None,None,None,None,None,None), + (1,4,"AdwToastOverlay","ToastOverlay",1,None,None,None,2,None,None), + (1,21,"AdwNavigationSplitView","SplitView",4,None,None,None,-1,None,None), + (1,34,"AdwNavigationPage",None,21,None,None,None,-1,None,None), + (1,35,"AdwNavigationPage",None,21,None,None,None,-1,None,None), + (1,52,"AdwToolbarView",None,34,None,None,None,-1,None,None), + (1,57,"AdwHeaderBar",None,52,None,"top",None,1,None,None), + (1,58,"GtkSpinner","WorkSpinner",57,None,"end",None,None,None,None), + (1,60,"GtkStack","PeersStack",52,None,None,None,-1,None,None), + (1,61,"AdwToolbarView",None,35,None,None,None,-1,None,None), + (1,67,"AdwHeaderBar",None,61,None,"top",None,1,None,None), + (1,68,"GtkSwitch","StatusSwitch",67,None,"start",None,None,None,None), + (1,69,"GtkMenuButton","MainMenuButton",67,None,"end",None,1,"<property name=\"menu-model\">MainMenu</property>",None), + (1,71,"GtkStackSidebar",None,61,None,None,None,-1,None,None), + (1,72,"AdwBreakpoint",None,1,None,None,None,1,"<condition>max-width: 400sp</condition><setter object=\"SplitView\" property=\"collapsed\">True</setter>",None), + (2,3,"AdwStatusPage","Page",None,None,None,None,None,None,None), + (2,73,"AdwClamp",None,3,None,None,None,1,None,None), + (2,74,"GtkBox",None,73,None,None,None,None,None,None), + (2,75,"AdwPreferencesGroup","IPGroup",74,None,None,None,None,None,None), + (2,82,"AdwPreferencesGroup","SendFileGroup",74,None,None,None,3,None,None), + (2,83,"AdwActionRow","SendFileRow",82,None,None,None,None,None,None), + (2,110,"AdwPreferencesGroup","MiscGroup",74,None,None,None,6,None,None), + (2,135,"GtkDropTarget","DropTarget",None,None,None,None,None,None,None), + (2,136,"GtkButton","SendFileButton",83,None,None,None,None,None,None), + (2,137,"AdwPreferencesGroup","AdvertisedRoutesGroup",74,None,None,None,3,None,None), + (2,138,"AdwSwitchRow","ExitNodeRow",110,None,None,None,7,None,None), + (2,139,"AdwActionRow","OnlineRow",110,None,None,None,7,None,None), + (2,140,"GtkImage","Online",139,None,None,None,None,None,None), + (2,141,"AdwActionRow","LastSeenRow",110,None,None,None,7,None,None), + (2,142,"GtkLabel","LastSeen",141,None,None,None,None,None,None), + (2,143,"AdwActionRow","CreatedRow",110,None,None,None,7,None,None), + (2,144,"GtkLabel","Created",143,None,None,None,None,None,None), + (2,145,"AdwActionRow","LastWriteRow",110,None,None,None,7,None,None), + (2,146,"GtkLabel","LastWrite",145,None,None,None,None,None,None), + (2,147,"AdwActionRow","LastHandshakeRow",110,None,None,None,7,None,None), + (2,148,"GtkLabel","LastHandshake",147,None,None,None,None,None,None), + (2,149,"AdwActionRow","RxBytesRow",110,None,None,None,7,None,None), + (2,150,"GtkLabel","RxBytes",149,None,None,None,None,None,None), + (2,151,"AdwActionRow","TxBytesRow",110,None,None,None,7,None,None), + (2,152,"GtkLabel","TxBytes",151,None,None,None,None,None,None), + (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), + (4,1,"AdwStatusPage","Page",None,None,None,None,None,None,None), + (4,2,"AdwClamp",None,1,None,None,None,None,None,None), + (4,3,"GtkBox",None,2,None,None,None,None,None,None), + (4,4,"AdwPreferencesGroup","IPGroup",3,None,None,None,None,None,None), + (4,5,"AdwPreferencesGroup","OptionsGroup",3,None,None,None,1,None,None), + (4,12,"AdwPreferencesGroup","FilesGroup",3,None,None,None,2,None,None), + (4,16,"AdwPreferencesGroup","AdvertisedRoutesGroup",3,None,None,None,4,None,None), + (4,17,"GtkButton","AdvertiseRouteButton",16,None,None,None,None,None,None), + (4,18,"AdwPreferencesGroup","NetCheckGroup",3,None,None,None,5,None,None), + (4,19,"GtkButton","NetCheckButton",18,None,None,None,None,None,None), + (4,20,"AdwActionRow","LastNetCheckRow",18,None,None,None,None,None,None), + (4,21,"GtkLabel","LastNetCheck",20,None,None,None,None,None,None), + (4,22,"AdwActionRow","UDPRow",18,None,None,None,1,None,None), + (4,23,"GtkImage","UDP",22,None,None,None,None,None,None), + (4,24,"AdwActionRow","IPv4Row",18,None,None,None,2,None,None), + (4,25,"GtkImage","IPv4Icon",24,None,None,None,None,None,None), + (4,26,"GtkLabel","IPv4Addr",24,None,None,None,1,None,None), + (4,27,"AdwActionRow","IPv6Row",18,None,None,None,3,None,None), + (4,28,"GtkImage","IPv6Icon",27,None,None,None,None,None,None), + (4,29,"GtkLabel","IPv6Addr",27,None,None,None,1,None,None), + (4,30,"AdwActionRow","UPnPRow",18,None,None,None,4,None,None), + (4,31,"GtkImage","UPnP",30,None,None,None,None,None,None), + (4,32,"AdwActionRow","PMPRow",18,None,None,None,5,None,None), + (4,33,"GtkImage","PMP",32,None,None,None,None,None,None), + (4,34,"AdwActionRow","PCPRow",18,None,None,None,6,None,None), + (4,35,"GtkImage","PCP",34,None,None,None,None,None,None), + (4,36,"AdwActionRow","HairPinningRow",18,None,None,None,7,None,None), + (4,37,"GtkImage","HairPinning",36,None,None,None,None,None,None), + (4,38,"AdwActionRow","PreferredDERPRow",18,None,None,None,8,None,None), + (4,39,"GtkLabel","PreferredDERP",38,None,None,None,None,None,None), + (4,40,"AdwExpanderRow","DERPLatencies",18,None,None,None,9,None,None), + (4,41,"AdwSwitchRow","AdvertiseExitNodeRow",5,None,None,None,None,None,None), + (4,42,"AdwSwitchRow","AllowLANAccessRow",5,None,None,None,1,None,None), + (4,43,"AdwSwitchRow","AcceptRoutesRow",5,None,None,None,2,None,None), + (5,1,"AdwStatusPage","Page",None,None,None,None,None,None,None), + (5,2,"AdwClamp",None,1,None,None,None,None,None,None), + (5,3,"GtkBox",None,2,None,None,None,None,None,None), + (5,4,"AdwPreferencesGroup","ExitNodesGroup",3,None,None,None,None,None,None) (1,1,"AdwApplicationWindow","content","4",None,None,None,None,None,None,None,None,None), @@ -137,17 +131,6 @@ (2,83,"AdwPreferencesRow","title","Send a file to remote machine",None,None,None,None,None,None,None,None,None), (2,83,"GtkActionable","action-name","peer.sendfile",None,None,None,None,None,None,None,None,None), (2,110,"AdwPreferencesGroup","title","Misc.",None,None,None,None,None,None,None,None,None), - (2,111,"AdwActionRow","activatable-widget","112",None,None,None,None,None,None,None,None,None), - (2,111,"AdwPreferencesRow","title","Use as exit node",None,None,None,None,None,None,None,None,None), - (2,112,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None), - (2,112,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None), - (2,113,"AdwPreferencesRow","title","Online",None,None,None,None,None,None,None,None,None), - (2,115,"AdwPreferencesRow","title","Last seen",None,None,None,None,None,None,None,None,None), - (2,117,"AdwPreferencesRow","title","Created at",None,None,None,None,None,None,None,None,None), - (2,119,"AdwPreferencesRow","title","Last write",None,None,None,None,None,None,None,None,None), - (2,121,"AdwPreferencesRow","title","Last handshake",None,None,None,None,None,None,None,None,None), - (2,123,"AdwPreferencesRow","title","Bytes received",None,None,None,None,None,None,None,None,None), - (2,125,"AdwPreferencesRow","title","Bytes sent",None,None,None,None,None,None,None,None,None), (2,134,"GtkButton","has-frame","False",None,None,None,None,None,None,None,None,None), (2,134,"GtkButton","icon-name","list-add-symbolic",None,None,None,None,None,None,None,None,None), (2,135,"GtkDropTarget","actions","copy",None,None,None,None,None,None,None,None,None), @@ -156,40 +139,31 @@ (2,136,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None), (2,136,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None), (2,137,"AdwPreferencesGroup","title","Advertised Routes",None,None,None,None,None,None,None,None,None), + (2,138,"AdwPreferencesRow","title","Use as exit node",None,None,None,None,None,None,None,None,None), + (2,139,"AdwPreferencesRow","title","Online",None,None,None,None,None,None,None,None,None), + (2,141,"AdwPreferencesRow","title","Last seen",None,None,None,None,None,None,None,None,None), + (2,143,"AdwPreferencesRow","title","Created at",None,None,None,None,None,None,None,None,None), + (2,145,"AdwPreferencesRow","title","Last write",None,None,None,None,None,None,None,None,None), + (2,147,"AdwPreferencesRow","title","Last handshake",None,None,None,None,None,None,None,None,None), + (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,4,"AdwActionRow","activatable-widget","12",None,None,None,None,None,None,None,None,None), - (3,4,"AdwActionRow","subtitle","If enabled, an icon will be added to the system tray",None,None,None,None,None,None,None,None,None), - (3,4,"AdwPreferencesRow","title","Use Tray Icon",None,None,None,None,None,None,None,None,None), - (3,5,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None), - (3,5,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None), - (3,6,"AdwActionRow","subtitle","Interval, in seconds, at which to poll the Tailscale daemon",None,None,None,None,None,None,None,None,None), - (3,6,"AdwPreferencesRow","title","Polling Interval",None,None,None,None,None,None,None,None,None), - (3,7,"GtkSpinButton","adjustment","8",None,None,None,None,None,None,None,None,None), - (3,7,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None), - (3,7,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None), - (3,8,"GtkAdjustment","lower","0.5",None,None,None,None,None,None,None,None,None), - (3,8,"GtkAdjustment","step-increment","1.0",None,None,None,None,None,None,None,None,None), - (3,8,"GtkAdjustment","upper","100.0",None,None,None,None,None,None,None,None,None), - (3,8,"GtkAdjustment","value","5.0",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), + (3,15,"AdwPreferencesRow","title","Polling Interval",None,None,None,None,None,None,None,None,None), + (3,15,"AdwSpinRow","adjustment",None,None,None,None,None,16,None,None,None,None), + (3,16,"GtkAdjustment","lower","0.5",None,None,None,None,None,None,None,None,None), + (3,16,"GtkAdjustment","step-increment","1.0",None,None,None,None,None,None,None,None,None), + (3,16,"GtkAdjustment","upper","100.0",None,None,None,None,None,None,None,None,None), + (3,16,"GtkAdjustment","value","5.0",None,None,None,None,None,None,None,None,None), (4,3,"GtkBox","spacing","12",None,None,None,None,None,None,None,None,None), (4,3,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None), (4,4,"AdwPreferencesGroup","title","Tailscale IPs",None,None,None,None,None,None,None,None,None), (4,5,"AdwPreferencesGroup","title","Options",None,None,None,None,None,None,None,None,None), - (4,6,"AdwActionRow","activatable-widget","7",None,None,None,None,None,None,None,None,None), - (4,6,"AdwPreferencesRow","title","Advertise exit node",None,None,None,None,None,None,None,None,None), - (4,7,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None), - (4,7,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None), - (4,8,"AdwActionRow","activatable-widget","9",None,None,None,None,None,None,None,None,None), - (4,8,"AdwPreferencesRow","title","Allow LAN access",None,None,None,None,None,None,None,None,None), - (4,9,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None), - (4,9,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None), - (4,10,"AdwActionRow","activatable-widget","11",None,None,None,None,None,None,None,None,None), - (4,10,"AdwPreferencesRow","title","Accept routes",None,None,None,None,None,None,None,None,None), - (4,11,"GtkWidget","margin-bottom","12",None,None,None,None,None,None,None,None,None), - (4,11,"GtkWidget","margin-top","12",None,None,None,None,None,None,None,None,None), (4,12,"AdwPreferencesGroup","title","Files",None,None,None,None,None,None,None,None,None), (4,16,"AdwPreferencesGroup","header-suffix",None,None,None,None,None,17,None,None,None,None), (4,16,"AdwPreferencesGroup","title","Advertised Routes",None,None,None,None,None,None,None,None,None), @@ -220,6 +194,9 @@ (4,39,"GtkLabel","label","Never",None,None,None,None,None,None,None,None,None), (4,40,"AdwPreferencesRow","title","DERP Latencies",None,None,None,None,None,None,None,None,None), (4,40,"GtkWidget","visible","False",None,None,None,None,None,None,None,None,None), + (4,41,"AdwPreferencesRow","title","Advertise exit node",None,None,None,None,None,None,None,None,None), + (4,42,"AdwPreferencesRow","title","Allow LAN access",None,None,None,None,None,None,None,None,None), + (4,43,"AdwPreferencesRow","title","Accept routes",None,None,None,None,None,None,None,None,None), (5,1,"AdwStatusPage","title","Mullvad Exit Nodes",None,None,None,None,None,None,None,None,None), (5,3,"GtkBox","spacing","12",None,None,None,None,None,None,None,None,None), (5,3,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None)