From e4d1ad3d8c9881a27df6734ceec314a1119f3f9d Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 3 May 2019 12:24:05 +0200 Subject: ui: add toolbar after adding it to the tabs --- ui/managewindow.go | 2 +- ui/tray.go | 4 +- ui/tunnelspage.go | 162 ++++++++++++++++++++++++++++------------------------- 3 files changed, 89 insertions(+), 79 deletions(-) diff --git a/ui/managewindow.go b/ui/managewindow.go index 5797220a..5e14419a 100644 --- a/ui/managewindow.go +++ b/ui/managewindow.go @@ -63,7 +63,6 @@ func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) { mtw.tunnelsPage.updateConfView() win.SetForegroundWindow(mtw.Handle()) win.BringWindowToTop(mtw.Handle()) - mtw.logPage.scrollToBottom() } }) @@ -74,6 +73,7 @@ func NewManageTunnelsWindow() (*ManageTunnelsWindow, error) { return nil, err } mtw.tabs.Pages().Add(mtw.tunnelsPage.TabPage) + mtw.tunnelsPage.CreateToolbar() if mtw.logPage, err = NewLogPage(); err != nil { return nil, err diff --git a/ui/tray.go b/ui/tray.go index c440d8ad..44d63d2b 100644 --- a/ui/tray.go +++ b/ui/tray.go @@ -144,7 +144,7 @@ func (tray *Tray) addTunnelAction(tunnel *service.Tunnel) { if err != nil { tray.mtw.Synchronize(func() { tray.mtw.Show() - tray.mtw.tunnelsPage.tunnelsView.selectTunnel(tclosure.Name) + tray.mtw.tunnelsPage.listView.selectTunnel(tclosure.Name) tray.mtw.tabs.SetCurrentIndex(0) if oldState == service.TunnelUnknown { walk.MsgBox(tray.mtw, "Failed to determine tunnel state", err.Error(), walk.MsgBoxIconError) @@ -303,7 +303,7 @@ func (tray *Tray) UpdateFound() { func (tray *Tray) onManageTunnels() { if !tray.mtw.Visible() { - tray.mtw.tunnelsPage.tunnelsView.SelectFirstActiveTunnel() + tray.mtw.tunnelsPage.listView.SelectFirstActiveTunnel() tray.mtw.tabs.SetCurrentIndex(0) } tray.mtw.Show() diff --git a/ui/tunnelspage.go b/ui/tunnelspage.go index c2b390a5..8207099f 100644 --- a/ui/tunnelspage.go +++ b/ui/tunnelspage.go @@ -9,6 +9,7 @@ import ( "archive/zip" "fmt" "github.com/lxn/walk" + "github.com/lxn/win" "golang.zx2c4.com/wireguard/windows/conf" "golang.zx2c4.com/wireguard/windows/service" "io/ioutil" @@ -16,12 +17,15 @@ import ( "path/filepath" "sort" "strings" + "unsafe" ) type TunnelsPage struct { *walk.TabPage - tunnelsView *ListView + listView *ListView + listContainer walk.Container + listToolbar *walk.ToolBar confView *ConfView fillerButton *walk.PushButton fillerHandler func() @@ -45,29 +49,82 @@ func NewTunnelsPage() (*TunnelsPage, error) { tp.SetTitle("Tunnels") tp.SetLayout(walk.NewHBoxLayout()) - listContainer, _ := walk.NewComposite(tp) + tp.listContainer, _ = walk.NewComposite(tp) vlayout := walk.NewVBoxLayout() vlayout.SetMargins(walk.Margins{}) vlayout.SetSpacing(0) - listContainer.SetLayout(vlayout) + tp.listContainer.SetLayout(vlayout) //TODO: deal with remaining disposables in case the next line fails - if tp.tunnelsView, err = NewListView(listContainer); err != nil { + if tp.listView, err = NewListView(tp.listContainer); err != nil { return nil, err } + tp.currentTunnelContainer, _ = walk.NewComposite(tp) + vlayout = walk.NewVBoxLayout() + vlayout.SetMargins(walk.Margins{}) + tp.currentTunnelContainer.SetLayout(vlayout) + + tp.fillerContainer, _ = walk.NewComposite(tp) + tp.fillerContainer.SetVisible(false) + hlayout := walk.NewHBoxLayout() + hlayout.SetMargins(walk.Margins{}) + tp.fillerContainer.SetLayout(hlayout) + tp.fillerButton, _ = walk.NewPushButton(tp.fillerContainer) + buttonWidth := tp.DPI() * 2 //TODO: Use dynamic DPI + tp.fillerButton.SetMinMaxSize(walk.Size{buttonWidth, 0}, walk.Size{buttonWidth, 0}) + tp.fillerButton.Clicked().Attach(func() { + if tp.fillerHandler != nil { + tp.fillerHandler() + } + }) + + tp.confView, _ = NewConfView(tp.currentTunnelContainer) + + controlsContainer, _ := walk.NewComposite(tp.currentTunnelContainer) + controlsContainer.SetLayout(walk.NewHBoxLayout()) + controlsContainer.Layout().SetMargins(walk.Margins{}) + + walk.NewHSpacer(controlsContainer) + + editTunnel, _ := walk.NewPushButton(controlsContainer) + editTunnel.SetEnabled(false) + tp.listView.CurrentIndexChanged().Attach(func() { + editTunnel.SetEnabled(tp.listView.CurrentIndex() > -1) + }) + editTunnel.SetText("Edit") + editTunnel.Clicked().Attach(tp.onEditTunnel) + + disposables.Spare() + + //TODO: expose walk.TableView.itemCountChangedPublisher.Event() + tp.listView.Property("ItemCount").Changed().Attach(tp.onTunnelsChanged) + tp.listView.SelectedIndexesChanged().Attach(tp.onSelectedTunnelsChanged) + tp.listView.ItemActivated().Attach(tp.onTunnelsViewItemActivated) + tp.listView.CurrentIndexChanged().Attach(tp.updateConfView) + tp.listView.Load(false) + tp.onTunnelsChanged() + + return tp, nil +} + +func (tp *TunnelsPage) CreateToolbar() { + if tp.listToolbar != nil { + return + } + // HACK: Because of https://github.com/lxn/walk/issues/481 // we need to put the ToolBar into its own Composite. - toolBarContainer, _ := walk.NewComposite(listContainer) + toolBarContainer, _ := walk.NewComposite(tp.listContainer) hlayout := walk.NewHBoxLayout() hlayout.SetMargins(walk.Margins{}) toolBarContainer.SetLayout(hlayout) - listToolbar, _ := walk.NewToolBarWithOrientationAndButtonStyle(toolBarContainer, walk.Horizontal, walk.ToolBarButtonImageBeforeText) + tp.listToolbar, _ = walk.NewToolBarWithOrientationAndButtonStyle(toolBarContainer, walk.Horizontal, walk.ToolBarButtonImageBeforeText) imageSize := walk.Size{tp.DPI() / 6, tp.DPI() / 6} // Dividing by six is the same as dividing by 96 and multiplying by 16. TODO: Use dynamic DPI imageList, _ := walk.NewImageList(imageSize, walk.RGB(0, 0, 0)) - listToolbar.SetImageList(imageList) + tp.listToolbar.SetImageList(imageList) addMenu, _ := walk.NewMenu() tp.AddDisposable(addMenu) @@ -94,9 +151,9 @@ func NewTunnelsPage() (*TunnelsPage, error) { addMenuAction.SetText("Add Tunnel") addMenuAction.SetToolTip(importAction.Text()) addMenuAction.Triggered().Attach(tp.onImport) - listToolbar.Actions().Add(addMenuAction) + tp.listToolbar.Actions().Add(addMenuAction) - listToolbar.Actions().Add(walk.NewSeparatorAction()) + tp.listToolbar.Actions().Add(walk.NewSeparatorAction()) deleteAction := walk.NewAction() deleteActionIcon, _ := loadSystemIcon("shell32", 131) @@ -105,9 +162,8 @@ func NewTunnelsPage() (*TunnelsPage, error) { deleteAction.SetShortcut(walk.Shortcut{0, walk.KeyDelete}) deleteAction.SetToolTip("Remove selected tunnel(s)") deleteAction.Triggered().Attach(tp.onDelete) - listToolbar.Actions().Add(deleteAction) - - listToolbar.Actions().Add(walk.NewSeparatorAction()) + tp.listToolbar.Actions().Add(deleteAction) + tp.listToolbar.Actions().Add(walk.NewSeparatorAction()) exportAction := walk.NewAction() exportActionIcon, _ := loadSystemIcon("imageres", 165) // Or "shell32", 45? @@ -115,61 +171,15 @@ func NewTunnelsPage() (*TunnelsPage, error) { exportAction.SetImage(exportActionImage) exportAction.SetToolTip("Export all tunnels to zip...") exportAction.Triggered().Attach(tp.onExportTunnels) - listToolbar.Actions().Add(exportAction) - - listContainerWidth := listToolbar.MinSizeHint().Width + tp.DPI()/10 // TODO: calculate these margins correctly instead - listContainer.SetMinMaxSize(walk.Size{listContainerWidth, 0}, walk.Size{listContainerWidth, 0}) - - tp.currentTunnelContainer, _ = walk.NewComposite(tp) - vlayout = walk.NewVBoxLayout() - vlayout.SetMargins(walk.Margins{}) - tp.currentTunnelContainer.SetLayout(vlayout) - - tp.fillerContainer, _ = walk.NewComposite(tp) - tp.fillerContainer.SetVisible(false) - hlayout = walk.NewHBoxLayout() - hlayout.SetMargins(walk.Margins{}) - tp.fillerContainer.SetLayout(hlayout) - tp.fillerButton, _ = walk.NewPushButton(tp.fillerContainer) - buttonWidth := tp.DPI() * 2 //TODO: Use dynamic DPI - tp.fillerButton.SetMinMaxSize(walk.Size{buttonWidth, 0}, walk.Size{buttonWidth, 0}) - tp.fillerButton.Clicked().Attach(func() { - if tp.fillerHandler != nil { - tp.fillerHandler() - } - }) - - tp.confView, _ = NewConfView(tp.currentTunnelContainer) + tp.listToolbar.Actions().Add(exportAction) - controlsContainer, _ := walk.NewComposite(tp.currentTunnelContainer) - controlsContainer.SetLayout(walk.NewHBoxLayout()) - controlsContainer.Layout().SetMargins(walk.Margins{}) - - walk.NewHSpacer(controlsContainer) - - editTunnel, _ := walk.NewPushButton(controlsContainer) - editTunnel.SetEnabled(false) - tp.tunnelsView.CurrentIndexChanged().Attach(func() { - editTunnel.SetEnabled(tp.tunnelsView.CurrentIndex() > -1) - }) - editTunnel.SetText("Edit") - editTunnel.Clicked().Attach(tp.onEditTunnel) - - disposables.Spare() - - //TODO: expose walk.TableView.itemCountChangedPublisher.Event() - tp.tunnelsView.Property("ItemCount").Changed().Attach(tp.onTunnelsChanged) - tp.tunnelsView.SelectedIndexesChanged().Attach(tp.onSelectedTunnelsChanged) - tp.tunnelsView.ItemActivated().Attach(tp.onTunnelsViewItemActivated) - tp.tunnelsView.CurrentIndexChanged().Attach(tp.updateConfView) - tp.tunnelsView.Load(false) - tp.onTunnelsChanged() - - return tp, nil + var size win.SIZE + tp.listToolbar.SendMessage(win.TB_GETIDEALSIZE, win.FALSE, uintptr(unsafe.Pointer(&size))) + tp.listContainer.SetMinMaxSize(walk.Size{int(size.CX), 0}, walk.Size{int(size.CX), 0}) } func (tp *TunnelsPage) updateConfView() { - tp.confView.SetTunnel(tp.tunnelsView.CurrentTunnel()) + tp.confView.SetTunnel(tp.listView.CurrentTunnel()) } func (tp *TunnelsPage) importFiles(paths []string) { @@ -268,7 +278,7 @@ func (tp *TunnelsPage) importFiles(paths []string) { } configCount++ } - tp.tunnelsView.Load(true) + tp.listView.Load(true) m, n := configCount, len(unparsedConfigs) switch { @@ -288,7 +298,7 @@ func (tp *TunnelsPage) exportTunnels(filePath string) { writeFileWithOverwriteHandling(tp.Form(), filePath, func(file *os.File) error { writer := zip.NewWriter(file) - for _, tunnel := range tp.tunnelsView.model.tunnels { + for _, tunnel := range tp.listView.model.tunnels { cfg, err := tunnel.StoredConfig() if err != nil { return fmt.Errorf("onExportTunnels: tunnel.StoredConfig failed: %v", err) @@ -331,7 +341,7 @@ func (tp *TunnelsPage) onTunnelsViewItemActivated() { if err != nil || (globalState != service.TunnelStarted && globalState != service.TunnelStopped) { return } - oldState, err := tp.tunnelsView.CurrentTunnel().Toggle() + oldState, err := tp.listView.CurrentTunnel().Toggle() if err != nil { tp.Synchronize(func() { if oldState == service.TunnelUnknown { @@ -348,7 +358,7 @@ func (tp *TunnelsPage) onTunnelsViewItemActivated() { } func (tp *TunnelsPage) onEditTunnel() { - tunnel := tp.tunnelsView.CurrentTunnel() + tunnel := tp.listView.CurrentTunnel() if tunnel == nil { // Misfired event? return @@ -375,7 +385,7 @@ func (tp *TunnelsPage) onAddTunnel() { } func (tp *TunnelsPage) onDelete() { - indices := tp.tunnelsView.SelectedIndexes() + indices := tp.listView.SelectedIndexes() if len(indices) == 0 { return } @@ -384,7 +394,7 @@ func (tp *TunnelsPage) onDelete() { if len(indices) > 1 { topic = fmt.Sprintf("%d tunnels", len(indices)) } else { - topic = fmt.Sprintf("ā€˜%sā€™", tp.tunnelsView.model.tunnels[0].Name) + topic = fmt.Sprintf("ā€˜%sā€™", tp.listView.model.tunnels[0].Name) } if walk.DlgCmdNo == walk.MsgBox( tp.Form(), @@ -395,24 +405,24 @@ func (tp *TunnelsPage) onDelete() { } selectTunnelAfter := "" - if len(indices) < len(tp.tunnelsView.model.tunnels) { + if len(indices) < len(tp.listView.model.tunnels) { sort.Ints(indices) max := 0 for i, idx := range indices { - if idx+1 < len(tp.tunnelsView.model.tunnels) && (i+1 == len(indices) || idx+1 != indices[i+1]) { + if idx+1 < len(tp.listView.model.tunnels) && (i+1 == len(indices) || idx+1 != indices[i+1]) { max = idx + 1 } else if idx-1 >= 0 && (i == 0 || idx-1 != indices[i-1]) { max = idx - 1 } } - selectTunnelAfter = tp.tunnelsView.model.tunnels[max].Name + selectTunnelAfter = tp.listView.model.tunnels[max].Name } for _, i := range indices { - tp.deleteTunnel(&tp.tunnelsView.model.tunnels[i]) + tp.deleteTunnel(&tp.listView.model.tunnels[i]) } if len(selectTunnelAfter) > 0 { - tp.tunnelsView.selectTunnel(selectTunnelAfter) + tp.listView.selectTunnel(selectTunnelAfter) } } @@ -461,14 +471,14 @@ func (tp *TunnelsPage) swapFiller(enabled bool) bool { } func (tp *TunnelsPage) onTunnelsChanged() { - if tp.swapFiller(tp.tunnelsView.model.RowCount() == 0) { + if tp.swapFiller(tp.listView.model.RowCount() == 0) { tp.fillerButton.SetText("Import tunnel(s) from file") tp.fillerHandler = tp.onImport } } func (tp *TunnelsPage) onSelectedTunnelsChanged() { - indices := tp.tunnelsView.SelectedIndexes() + indices := tp.listView.SelectedIndexes() if tp.swapFiller(len(indices) > 1) { tp.fillerButton.SetText(fmt.Sprintf("Delete %d tunnels", len(indices))) tp.fillerHandler = tp.onDelete -- cgit v1.2.3-59-g8ed1b