Skip to content

Commit 07cefb6

Browse files
committed
Menus and Menu Bars
* New and completed widgets: Menu, MenuButton and MenuBar. * MenuButton is a kind of Button that opens a popup Menu when clicked. * MenuBar is a container of buttons designed to be attached to the top of an application window ("File, Edit, View, Help") * Supervisor manages the popup menus with its new concept of a Modal Widget. Modal widgets take exclusive event priority for all mouse and key events. The pop-up menu is a modal window, which means you must click an option inside the menu OR clicking outside the menu will close it and eat your click event (widgets outside the modal don't receive events, but the modal itself gets an event that you've done this).
1 parent d27636e commit 07cefb6

20 files changed

+1140
-121
lines changed

README.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -138,25 +138,21 @@ most complex.
138138
(drag it by its title bar, Close button, window focus, multiple overlapping
139139
windows, and so on).
140140
* [x] **Tooltip**: a mouse hover label attached to a widget.
141+
* [x] **MenuButton**: a button that opens a modal pop-up menu on click.
142+
* [x] **MenuBar**: a specialized Frame that groups a bunch of MenuButtons and
143+
provides a simple API to add menus and items to it.
144+
* [x] **Menu**: a frame full of clickable links and separators. Usually used as
145+
a modal pop-up by the MenuButton and MenuBar.
141146

142147
**Work in progress widgets:**
143148

144-
* [x] **Menu**: a frame with clickable menu items.
145-
* To be a base widget behind right-click context menus, pull-down menus
146-
from a MenuBar, options from a SelectBox and so on.
147-
* Powered by Frame and Button but with a nice API for composing menu
148-
actions.
149-
* Partially implemented so far.
150-
* [ ] **MenuButton**: a Button that opens a Menu when clicked.
151-
* [ ] **MenuBar**: a Frame that houses many MenuButtons, intended for the
152-
main menu at the top of a UI window (File, Edit, Help, etc.).
153149
* [ ] **Scrollbar**: a Frame including a trough, scroll buttons and a
154150
draggable slider.
151+
* [ ] **SelectBox:** a kind of MenuButton that lets the user choose a value
152+
from a list of possible values, bound to a string variable.
155153

156154
**Wish list for the longer-term future:**
157155

158-
* [ ] **SelectBox:** a kind of MenuButton that lets the user choose a value
159-
from a list of possible values, bound to a string variable.
160156
* [ ] **TextBox:** an editable text field that the user can focus and type
161157
a value into.
162158
* Would depend on the WindowManager to manage focus for the widgets.

debug.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import (
55
"strings"
66
)
77

8+
// PrintWidgetTree prints a widget tree to console.
9+
func PrintWidgetTree(root Widget) {
10+
fmt.Printf("--- Widget Tree of %s ---\n", root)
11+
for _, row := range WidgetTree(root) {
12+
fmt.Println(row)
13+
}
14+
}
15+
816
// WidgetTree returns a string representing the tree of widgets starting
917
// at a given widget.
1018
func WidgetTree(root Widget) []string {

docs/menus-1.png

18 KB
Loading

docs/menus-2.png

19.9 KB
Loading

eg/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Examples for go/ui
2+
3+
* [Hello, World!](hello-world/): a basic UI demo.
4+
* [Frame Place()](frame-place/): demonstrates using the Place() layout management
5+
option for Frame widgets.]
6+
* [Window Manager](windows/): demonstrates the Window widget and window
7+
management features of the Supervisor.
8+
* [Tooltip](tooltip/): demonstrates the Tooltip widget on a variety of buttons
9+
scattered around the window.
10+
* [Menus](menus/): demonstrates various Menu Buttons and a Menu Bar.

eg/menus/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.PHONY: run
2+
run:
3+
go run main.go
4+
5+
.PHONY: wasm
6+
wasm:
7+
GOOS=js GOARCH=wasm go build -v -o windows.wasm main_wasm.go
8+
9+
.PHONY: wasm-serve
10+
wasm-serve: wasm
11+
../wasm-common/serve.sh

eg/menus/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Menu Example
2+
3+
This example shows off the Menu, MenuButton, and MenuBar widgets.
4+
5+
* MenuButton is your basic button that pops up a Menu when clicked.
6+
* MenuBar is a specialized Frame that attaches to the top of the parent
7+
(usually the window) and provides a simple API to add menus and items.
8+
* Menu is the underlying "pop-up and select an item" widget.
9+
10+
## Running It
11+
12+
From your terminal, just type `go run main.go` or `make run` from this
13+
example's directory.
14+
15+
## Screenshots
16+
17+
![Screenshot 1](../../docs/menus-1.png)
18+
19+
![Screenshot 2](../../docs/menus-2.png)

eg/menus/main.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"git.kirsle.net/go/render"
8+
"git.kirsle.net/go/render/event"
9+
"git.kirsle.net/go/render/sdl"
10+
"git.kirsle.net/go/ui"
11+
)
12+
13+
// Program globals.
14+
var (
15+
// Size of the MainWindow.
16+
Width = 640
17+
Height = 480
18+
19+
BGColor = render.White
20+
)
21+
22+
func init() {
23+
sdl.DefaultFontFilename = "../DejaVuSans.ttf"
24+
}
25+
26+
func main() {
27+
mw, err := ui.NewMainWindow("Menu Demo", Width, Height)
28+
if err != nil {
29+
panic(err)
30+
}
31+
32+
setupMainMenu(mw)
33+
34+
// Menu button in middle of window.
35+
{
36+
btn := ui.NewMenuButton("MenuBtn", ui.NewLabel(ui.Label{
37+
Text: "Click me!",
38+
}))
39+
btn.Supervise(mw.Supervisor())
40+
mw.Place(btn, ui.Place{
41+
Center: true,
42+
Middle: true,
43+
})
44+
45+
for _, label := range []string{
46+
"MenuButtons open menus",
47+
"when clicked. MenuBar is ",
48+
"a Frame of MenuButtons",
49+
"attached to the top of the",
50+
"window.",
51+
"",
52+
"They all provide a nice API",
53+
"to insert menus and items.",
54+
} {
55+
label := label
56+
if label == "" {
57+
btn.AddSeparator()
58+
continue
59+
}
60+
btn.AddItem(label, func() {
61+
fmt.Printf("Button '%s' clicked!\n", label)
62+
})
63+
}
64+
65+
}
66+
67+
// Menu button on the bottom right of screen.
68+
{
69+
btn := ui.NewMenuButton("BrBtn", ui.NewLabel(ui.Label{
70+
Text: "Fruits",
71+
}))
72+
btn.Supervise(mw.Supervisor())
73+
mw.Place(btn, ui.Place{
74+
Right: 20,
75+
Bottom: 20,
76+
})
77+
78+
btn.AddItem("Apples", func() {})
79+
btn.AddItem("Oranges", func() {})
80+
btn.AddItem("Bananas", func() {})
81+
btn.AddItem("Pears", func() {})
82+
}
83+
84+
// Menu button on the bottom left of screen.
85+
{
86+
btn := ui.NewMenuButton("BlBtn", ui.NewLabel(ui.Label{
87+
Text: "Set Window Color",
88+
}))
89+
btn.Supervise(mw.Supervisor())
90+
mw.Place(btn, ui.Place{
91+
Left: 20,
92+
Bottom: 20,
93+
})
94+
95+
setBg := func(color render.Color) func() {
96+
return func() {
97+
BGColor = color
98+
}
99+
}
100+
101+
// Really fancy buttons.
102+
var colors = []struct {
103+
label string
104+
hex string
105+
color render.Color
106+
}{
107+
{"Black", "#000", render.Black},
108+
{"Red", "#F00", render.Red},
109+
{"Yellow", "#FF0", render.Yellow},
110+
{"Green", "#0F0", render.Green},
111+
{"Cyan", "#0FF", render.Cyan},
112+
{"Blue", "#00F", render.Blue},
113+
{"Magenta", "#F0F", render.Magenta},
114+
{"White", "#FFF", render.White},
115+
}
116+
for _, opt := range colors {
117+
item := btn.AddItemAccel(opt.label, opt.hex, setBg(opt.color))
118+
item.SetBackground(opt.color.Lighten(128))
119+
}
120+
121+
// btn.AddItemAccel("Black", "#000", setBg(render.White))
122+
// btn.AddItemAccel("Red", "#F00", setBg(render.Red))
123+
// btn.AddItemAccel("Yellow", "#FF0", setBg(render.Yellow))
124+
// btn.AddItemAccel("Green", "#0F0", setBg(render.Green))
125+
// btn.AddItemAccel("Cyan", "#0FF", setBg(render.Cyan))
126+
// btn.AddItemAccel("Blue", "#00F", setBg(render.Blue))
127+
// btn.AddItemAccel("Magenta", "#F0F", setBg(render.Magenta))
128+
// btn.AddItemAccel("White", "#FFF", setBg(render.White))
129+
}
130+
131+
// The "Long Menu" on the middle left side
132+
{
133+
btn := ui.NewMenuButton("BlBtn", ui.NewLabel(ui.Label{
134+
Text: "Tall Growing Menu",
135+
}))
136+
btn.Supervise(mw.Supervisor())
137+
mw.Place(btn, ui.Place{
138+
Left: 20,
139+
Middle: true,
140+
})
141+
142+
var id int
143+
btn.AddItem("Add New Option", func() {
144+
id++
145+
id := id
146+
btn.AddItem(fmt.Sprintf("Menu Item #%d", id), func() {
147+
fmt.Printf("Chosen menu item %d\n", id)
148+
})
149+
})
150+
151+
btn.AddSeparator()
152+
}
153+
154+
mw.OnLoop(func(e *event.State) {
155+
mw.SetBackground(BGColor)
156+
if e.Up {
157+
fmt.Println("Supervised widgets:")
158+
for widg := range mw.Supervisor().Widgets() {
159+
fmt.Printf("%+v\n", widg)
160+
}
161+
}
162+
if e.Escape {
163+
os.Exit(0)
164+
}
165+
})
166+
167+
mw.MainLoop()
168+
}
169+
170+
func setupMainMenu(mw *ui.MainWindow) {
171+
bar := ui.NewMenuBar("Main Menu")
172+
173+
fileMenu := bar.AddMenu("File")
174+
fileMenu.AddItemAccel("New", "Ctrl-N", func() {
175+
fmt.Println("Chose File->New")
176+
})
177+
fileMenu.AddItemAccel("Open", "Ctrl-O", func() {
178+
fmt.Println("Chose File->Open")
179+
})
180+
fileMenu.AddSeparator()
181+
fileMenu.AddItemAccel("Exit", "Alt-F4", func() {
182+
fmt.Println("Chose File->Exit")
183+
os.Exit(0)
184+
})
185+
186+
editMenu := bar.AddMenu("Edit")
187+
editMenu.AddItemAccel("Undo", "Ctrl-Z", func() {})
188+
editMenu.AddItemAccel("Redo", "Shift-Ctrl-Z", func() {})
189+
editMenu.AddSeparator()
190+
editMenu.AddItemAccel("Cut", "Ctrl-X", func() {})
191+
editMenu.AddItemAccel("Copy", "Ctrl-C", func() {})
192+
editMenu.AddItemAccel("Paste", "Ctrl-V", func() {})
193+
editMenu.AddSeparator()
194+
editMenu.AddItem("Settings...", func() {})
195+
196+
viewMenu := bar.AddMenu("View")
197+
viewMenu.AddItemAccel("Toggle Full Screen", "F11", func() {})
198+
199+
helpMenu := bar.AddMenu("Help")
200+
helpMenu.AddItemAccel("Contents", "F1", func() {})
201+
helpMenu.AddItem("About", func() {})
202+
203+
bar.Supervise(mw.Supervisor())
204+
bar.Compute(mw.Engine)
205+
mw.Pack(bar, bar.PackTop())
206+
207+
fmt.Printf("Setup MenuBar: %s\n", bar.Size())
208+
}

0 commit comments

Comments
 (0)