mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 21:10:54 +08:00
InsertBefore/InsertAfter supported
This commit is contained in:
parent
e8bb950e06
commit
7e42052da0
@ -7,25 +7,25 @@ import (
|
||||
// CustomLogger defines what a user can do with a logger
|
||||
type CustomLogger interface {
|
||||
// Writeln writes directly to the output with no log level plus line ending
|
||||
Writeln(message string) error
|
||||
Writeln(message string)
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
Write(message string) error
|
||||
Write(message string)
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
Trace(format string, args ...interface{}) error
|
||||
Trace(format string, args ...interface{})
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
Debug(format string, args ...interface{}) error
|
||||
Debug(format string, args ...interface{})
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
Info(format string, args ...interface{}) error
|
||||
Info(format string, args ...interface{})
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
Warning(format string, args ...interface{}) error
|
||||
Warning(format string, args ...interface{})
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
Error(format string, args ...interface{}) error
|
||||
Error(format string, args ...interface{})
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
Fatal(format string, args ...interface{})
|
||||
@ -49,43 +49,43 @@ func newcustomLogger(logger *Logger, name string) *customLogger {
|
||||
|
||||
// Writeln writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *customLogger) Writeln(message string) error {
|
||||
return l.logger.Writeln(message)
|
||||
func (l *customLogger) Writeln(message string) {
|
||||
l.logger.Writeln(message)
|
||||
}
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
func (l *customLogger) Write(message string) error {
|
||||
return l.logger.Write(message)
|
||||
func (l *customLogger) Write(message string) {
|
||||
l.logger.Write(message)
|
||||
}
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *customLogger) Trace(format string, args ...interface{}) error {
|
||||
func (l *customLogger) Trace(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Trace(format, args...)
|
||||
l.logger.Trace(format, args...)
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *customLogger) Debug(format string, args ...interface{}) error {
|
||||
func (l *customLogger) Debug(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Debug(format, args...)
|
||||
l.logger.Debug(format, args...)
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *customLogger) Info(format string, args ...interface{}) error {
|
||||
func (l *customLogger) Info(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Info(format, args...)
|
||||
l.logger.Info(format, args...)
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *customLogger) Warning(format string, args ...interface{}) error {
|
||||
func (l *customLogger) Warning(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Warning(format, args...)
|
||||
l.logger.Warning(format, args...)
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *customLogger) Error(format string, args ...interface{}) error {
|
||||
func (l *customLogger) Error(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Error(format, args...)
|
||||
l.logger.Error(format, args...)
|
||||
|
||||
}
|
||||
|
||||
|
@ -46,67 +46,60 @@ func (l *Logger) SetLogLevel(level LogLevel) {
|
||||
|
||||
// Writeln writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Writeln(message string) error {
|
||||
return l.output.Print(message)
|
||||
func (l *Logger) Writeln(message string) {
|
||||
l.output.Print(message)
|
||||
}
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
func (l *Logger) Write(message string) error {
|
||||
return l.output.Print(message)
|
||||
func (l *Logger) Write(message string) {
|
||||
l.output.Print(message)
|
||||
}
|
||||
|
||||
// Print writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Print(message string) error {
|
||||
return l.Write(message)
|
||||
func (l *Logger) Print(message string) {
|
||||
l.Write(message)
|
||||
}
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *Logger) Trace(format string, args ...interface{}) error {
|
||||
func (l *Logger) Trace(format string, args ...interface{}) {
|
||||
if l.logLevel <= logger.TRACE {
|
||||
return l.output.Trace(fmt.Sprintf(format, args...))
|
||||
l.output.Trace(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *Logger) Debug(format string, args ...interface{}) error {
|
||||
func (l *Logger) Debug(format string, args ...interface{}) {
|
||||
if l.logLevel <= logger.DEBUG {
|
||||
return l.output.Debug(fmt.Sprintf(format, args...))
|
||||
l.output.Debug(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *Logger) Info(format string, args ...interface{}) error {
|
||||
func (l *Logger) Info(format string, args ...interface{}) {
|
||||
if l.logLevel <= logger.INFO {
|
||||
return l.output.Info(fmt.Sprintf(format, args...))
|
||||
l.output.Info(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *Logger) Warning(format string, args ...interface{}) error {
|
||||
func (l *Logger) Warning(format string, args ...interface{}) {
|
||||
if l.logLevel <= logger.WARNING {
|
||||
return l.output.Warning(fmt.Sprintf(format, args...))
|
||||
l.output.Warning(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *Logger) Error(format string, args ...interface{}) error {
|
||||
func (l *Logger) Error(format string, args ...interface{}) {
|
||||
if l.logLevel <= logger.ERROR {
|
||||
return l.output.Error(fmt.Sprintf(format, args...))
|
||||
l.output.Error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
func (l *Logger) Fatal(format string, args ...interface{}) {
|
||||
err := l.output.Fatal(fmt.Sprintf(format, args...))
|
||||
// Not much we can do but print it out before exiting
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
l.output.Fatal(fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -168,25 +168,24 @@ func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) {
|
||||
}
|
||||
|
||||
// Publish sends the given message on the service bus
|
||||
func (s *ServiceBus) Publish(topic string, data interface{}) error {
|
||||
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
||||
// Prevent publish when closed
|
||||
if s.closed {
|
||||
return fmt.Errorf("cannot call publish on closed servicebus")
|
||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
||||
return
|
||||
}
|
||||
|
||||
message := NewMessage(topic, data)
|
||||
s.messageQueue <- message
|
||||
return nil
|
||||
}
|
||||
|
||||
// PublishForTarget sends the given message on the service bus for the given target
|
||||
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) error {
|
||||
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
||||
// Prevent publish when closed
|
||||
if s.closed {
|
||||
return fmt.Errorf("cannot call publish on closed servicebus")
|
||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
||||
return
|
||||
}
|
||||
|
||||
message := NewMessageForTarget(topic, data, target)
|
||||
s.messageQueue <- message
|
||||
return nil
|
||||
}
|
||||
|
@ -36,9 +36,10 @@ func (m *MenuItem) Parent() *MenuItem {
|
||||
// submenu, then this method will not add the item and
|
||||
// simply return false.
|
||||
func (m *MenuItem) Append(item *MenuItem) bool {
|
||||
if m.Type != SubmenuType {
|
||||
if !m.isSubMenu() {
|
||||
return false
|
||||
}
|
||||
item.parent = m
|
||||
m.SubMenu = append(m.SubMenu, item)
|
||||
return true
|
||||
}
|
||||
@ -48,9 +49,10 @@ func (m *MenuItem) Append(item *MenuItem) bool {
|
||||
// submenu, then this method will not add the item and
|
||||
// simply return false.
|
||||
func (m *MenuItem) Prepend(item *MenuItem) bool {
|
||||
if m.Type != SubmenuType {
|
||||
if !m.isSubMenu() {
|
||||
return false
|
||||
}
|
||||
item.parent = m
|
||||
m.SubMenu = append([]*MenuItem{item}, m.SubMenu...)
|
||||
return true
|
||||
}
|
||||
@ -80,7 +82,7 @@ func (m *MenuItem) removeByID(id string) bool {
|
||||
m.SubMenu = append(m.SubMenu[:index], m.SubMenu[index+1:]...)
|
||||
return true
|
||||
}
|
||||
if item.Type == SubmenuType {
|
||||
if item.isSubMenu() {
|
||||
result := item.removeByID(id)
|
||||
if result == true {
|
||||
return result
|
||||
@ -90,6 +92,120 @@ func (m *MenuItem) removeByID(id string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// InsertAfter attempts to add the given item after this item in the parent
|
||||
// menu. If there is no parent menu (we are a top level menu) then false is
|
||||
// returned
|
||||
func (m *MenuItem) InsertAfter(item *MenuItem) bool {
|
||||
|
||||
// We need to find my parent
|
||||
if m.parent == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get my parent to insert the item
|
||||
return m.parent.insertNewItemAfterGivenItem(m, item)
|
||||
}
|
||||
|
||||
// InsertBefore attempts to add the given item before this item in the parent
|
||||
// menu. If there is no parent menu (we are a top level menu) then false is
|
||||
// returned
|
||||
func (m *MenuItem) InsertBefore(item *MenuItem) bool {
|
||||
|
||||
// We need to find my parent
|
||||
if m.parent == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get my parent to insert the item
|
||||
return m.parent.insertNewItemBeforeGivenItem(m, item)
|
||||
}
|
||||
|
||||
// insertNewItemAfterGivenItem will insert the given item after the given target
|
||||
// in this item's submenu. If we are not a submenu,
|
||||
// then something bad has happened :/
|
||||
func (m *MenuItem) insertNewItemAfterGivenItem(target *MenuItem,
|
||||
newItem *MenuItem) bool {
|
||||
|
||||
if !m.isSubMenu() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Find the index of the target
|
||||
targetIndex := m.getItemIndex(target)
|
||||
if targetIndex == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Insert element into slice
|
||||
return m.insertItemAtIndex(targetIndex+1, newItem)
|
||||
}
|
||||
|
||||
// insertNewItemBeforeGivenItem will insert the given item before the given
|
||||
// target in this item's submenu. If we are not a submenu, then something bad
|
||||
// has happened :/
|
||||
func (m *MenuItem) insertNewItemBeforeGivenItem(target *MenuItem,
|
||||
newItem *MenuItem) bool {
|
||||
|
||||
if !m.isSubMenu() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Find the index of the target
|
||||
targetIndex := m.getItemIndex(target)
|
||||
if targetIndex == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Insert element into slice
|
||||
return m.insertItemAtIndex(targetIndex, newItem)
|
||||
}
|
||||
|
||||
func (m *MenuItem) isSubMenu() bool {
|
||||
return m.Type == SubmenuType
|
||||
}
|
||||
|
||||
// getItemIndex returns the index of the given target relative to this menu
|
||||
func (m *MenuItem) getItemIndex(target *MenuItem) int {
|
||||
|
||||
// This should only be called on submenus
|
||||
if !m.isSubMenu() {
|
||||
return -1
|
||||
}
|
||||
|
||||
// hunt down that bad boy
|
||||
for index, item := range m.SubMenu {
|
||||
if item == target {
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// insertItemAtIndex attempts to insert the given item into the submenu at
|
||||
// the given index
|
||||
// Credit: https://stackoverflow.com/a/61822301
|
||||
func (m *MenuItem) insertItemAtIndex(index int, target *MenuItem) bool {
|
||||
|
||||
// If index is OOB, return false
|
||||
if index > len(m.SubMenu) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Save parent reference
|
||||
target.parent = m
|
||||
|
||||
// If index is last item, then just regular append
|
||||
if index == len(m.SubMenu) {
|
||||
m.SubMenu = append(m.SubMenu, target)
|
||||
return true
|
||||
}
|
||||
|
||||
m.SubMenu = append(m.SubMenu[:index+1], m.SubMenu[index:]...)
|
||||
m.SubMenu[index] = target
|
||||
return true
|
||||
}
|
||||
|
||||
// Text is a helper to create basic Text menu items
|
||||
func Text(label string, id string) *MenuItem {
|
||||
return TextWithAccelerator(label, id, nil)
|
||||
@ -159,3 +275,20 @@ func SubMenu(label string, items []*MenuItem) *MenuItem {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SubMenuWithID is a helper to create Submenus with an ID
|
||||
func SubMenuWithID(label string, id string, items []*MenuItem) *MenuItem {
|
||||
result := &MenuItem{
|
||||
Label: label,
|
||||
SubMenu: items,
|
||||
ID: id,
|
||||
Type: SubmenuType,
|
||||
}
|
||||
|
||||
// Fix up parent pointers
|
||||
for _, item := range items {
|
||||
item.parent = result
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
@ -13,8 +14,10 @@ import (
|
||||
type Menu struct {
|
||||
runtime *wails.Runtime
|
||||
|
||||
dynamicMenuCounter int
|
||||
lock sync.Mutex
|
||||
dynamicMenuCounter int
|
||||
lock sync.Mutex
|
||||
dynamicMenuItems map[string]*menu.MenuItem
|
||||
anotherDynamicMenuCounter int
|
||||
}
|
||||
|
||||
// WailsInit is called at application startup
|
||||
@ -34,6 +37,9 @@ func (m *Menu) WailsInit(runtime *wails.Runtime) error {
|
||||
fmt.Printf("We can use UTF-8 IDs: %s\n", mi.Label)
|
||||
})
|
||||
|
||||
// Create dynamic menu items 2 submenu
|
||||
m.createDynamicMenuTwo()
|
||||
|
||||
// Setup dynamic menus
|
||||
m.runtime.Menu.On("Add Menu Item", m.addMenu)
|
||||
return nil
|
||||
@ -111,6 +117,102 @@ func (m *Menu) removeMenu(_ *menu.MenuItem) {
|
||||
m.runtime.Menu.Update()
|
||||
}
|
||||
|
||||
func (m *Menu) createDynamicMenuTwo() {
|
||||
|
||||
// Create our submenu
|
||||
dm2 := menu.SubMenu("Dynamic Menus 2", []*menu.MenuItem{
|
||||
menu.TextWithAccelerator("Insert Before Random Menu Item",
|
||||
"Insert Before Random", menu.CmdOrCtrlAccel("]")),
|
||||
menu.TextWithAccelerator("Insert After Random Menu Item",
|
||||
"Insert After Random", menu.CmdOrCtrlAccel("[")),
|
||||
menu.Separator(),
|
||||
})
|
||||
|
||||
m.runtime.Menu.On("Insert Before Random", m.insertBeforeRandom)
|
||||
m.runtime.Menu.On("Insert After Random", m.insertAfterRandom)
|
||||
|
||||
// Initialise out map
|
||||
m.dynamicMenuItems = make(map[string]*menu.MenuItem)
|
||||
|
||||
// Create some random menu items
|
||||
m.anotherDynamicMenuCounter = 5
|
||||
for index := 0; index < m.anotherDynamicMenuCounter; index++ {
|
||||
text := "Other Dynamic Menu Item " + strconv.Itoa(index+1)
|
||||
item := menu.Text(text, text)
|
||||
m.dynamicMenuItems[text] = item
|
||||
dm2.Append(item)
|
||||
}
|
||||
|
||||
// Insert this menu after Dynamic Menu Item 1
|
||||
dm1 := m.runtime.Menu.GetByID("Dynamic Menus 1")
|
||||
dm1.InsertAfter(dm2)
|
||||
m.runtime.Menu.Update()
|
||||
}
|
||||
|
||||
func (m *Menu) insertBeforeRandom(_ *menu.MenuItem) {
|
||||
|
||||
// Lock because this method will be called in a goroutine
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
// Pick a random menu
|
||||
var randomItemID string
|
||||
var count int
|
||||
var random = rand.Intn(len(m.dynamicMenuItems))
|
||||
for randomItemID = range m.dynamicMenuItems {
|
||||
if count == random {
|
||||
break
|
||||
}
|
||||
count++
|
||||
}
|
||||
m.anotherDynamicMenuCounter++
|
||||
text := "Other Dynamic Menu Item " + strconv.Itoa(
|
||||
m.anotherDynamicMenuCounter+1)
|
||||
newItem := menu.Text(text, text)
|
||||
m.dynamicMenuItems[text] = newItem
|
||||
|
||||
item := m.runtime.Menu.GetByID(randomItemID)
|
||||
|
||||
m.runtime.Log.Info(fmt.Sprintf(
|
||||
"Inserting menu item '%s' before menu item '%s'", newItem.Label,
|
||||
item.Label))
|
||||
|
||||
item.InsertBefore(newItem)
|
||||
m.runtime.Menu.Update()
|
||||
}
|
||||
|
||||
func (m *Menu) insertAfterRandom(_ *menu.MenuItem) {
|
||||
|
||||
// Lock because this method will be called in a goroutine
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
// Pick a random menu
|
||||
var randomItemID string
|
||||
var count int
|
||||
var random = rand.Intn(len(m.dynamicMenuItems))
|
||||
for randomItemID = range m.dynamicMenuItems {
|
||||
if count == random {
|
||||
break
|
||||
}
|
||||
count++
|
||||
}
|
||||
m.anotherDynamicMenuCounter++
|
||||
text := "Other Dynamic Menu Item " + strconv.Itoa(
|
||||
m.anotherDynamicMenuCounter+1)
|
||||
newItem := menu.Text(text, text)
|
||||
|
||||
item := m.runtime.Menu.GetByID(randomItemID)
|
||||
m.dynamicMenuItems[text] = newItem
|
||||
|
||||
m.runtime.Log.Info(fmt.Sprintf(
|
||||
"Inserting menu item '%s' after menu item '%s'", newItem.Label,
|
||||
item.Label))
|
||||
|
||||
item.InsertAfter(newItem)
|
||||
m.runtime.Menu.Update()
|
||||
}
|
||||
|
||||
func createApplicationMenu() *menu.Menu {
|
||||
|
||||
// Create menu
|
||||
@ -184,7 +286,7 @@ func createApplicationMenu() *menu.Menu {
|
||||
menu.TextWithAccelerator("Plus", "Plus", menu.Accel("+")),
|
||||
}),
|
||||
}),
|
||||
menu.SubMenu("Dynamic Menus", []*menu.MenuItem{
|
||||
menu.SubMenuWithID("Dynamic Menus 1", "Dynamic Menus 1", []*menu.MenuItem{
|
||||
menu.TextWithAccelerator("Add Menu Item", "Add Menu Item", menu.CmdOrCtrlAccel("+")),
|
||||
menu.Separator(),
|
||||
}),
|
||||
|
Loading…
Reference in New Issue
Block a user