Initial support for tray menus

This commit is contained in:
Lea Anthony 2021-02-13 20:26:21 +11:00
commit eac8880f6d
No known key found for this signature in database
GPG key ID: 33DAF7BB90A58405
9 changed files with 1847 additions and 27 deletions

View file

@ -212,7 +212,7 @@ func (a *App) Run() error {
// Generate backend.js
a.bindings.GenerateBackendJS()
err = a.bridge.Run(dispatcher, bindingDump, a.debug)
err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug)
a.logger.Trace("Bridge.Run() exited")
if err != nil {
return err

View file

@ -6,6 +6,8 @@ import (
"net/http"
"sync"
"github.com/wailsapp/wails/v2/internal/menumanager"
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
"github.com/gorilla/websocket"
@ -28,6 +30,9 @@ type Bridge struct {
// Dialog client
dialog *messagedispatcher.DispatchClient
// Menus
menumanager *menumanager.Manager
}
func NewBridge(myLogger *logger.Logger) *Bridge {
@ -47,13 +52,14 @@ func NewBridge(myLogger *logger.Logger) *Bridge {
return result
}
func (b *Bridge) Run(dispatcher *messagedispatcher.Dispatcher, bindings string, debug bool) error {
func (b *Bridge) Run(dispatcher *messagedispatcher.Dispatcher, menumanager *menumanager.Manager, bindings string, debug bool) error {
// Ensure we cancel the context when we shutdown
defer b.cancel()
b.bindings = bindings
b.dispatcher = dispatcher
b.menumanager = menumanager
// Setup dialog handler
dialogClient := NewDialogClient(b.myLogger)
@ -88,7 +94,7 @@ func (b *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
func (b *Bridge) startSession(conn *websocket.Conn) {
// Create a new session for this connection
s := newSession(conn, b.bindings, b.dispatcher, b.myLogger, b.ctx)
s := newSession(conn, b.menumanager, b.bindings, b.dispatcher, b.myLogger, b.ctx)
// Setup the close handler
conn.SetCloseHandler(func(int, string) error {

View file

@ -7,6 +7,8 @@ import (
"runtime"
"time"
"github.com/wailsapp/wails/v2/internal/menumanager"
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
"github.com/gorilla/websocket"
@ -35,16 +37,20 @@ type session struct {
// client
client *messagedispatcher.DispatchClient
// Menus
menumanager *menumanager.Manager
}
func newSession(conn *websocket.Conn, bindings string, dispatcher *messagedispatcher.Dispatcher, logger *logger.Logger, ctx context.Context) *session {
func newSession(conn *websocket.Conn, menumanager *menumanager.Manager, bindings string, dispatcher *messagedispatcher.Dispatcher, logger *logger.Logger, ctx context.Context) *session {
result := &session{
conn: conn,
bindings: bindings,
log: logger,
shutdown: make(chan bool),
writeChan: make(chan []byte, 100),
ctx: ctx,
conn: conn,
bindings: bindings,
log: logger,
shutdown: make(chan bool),
writeChan: make(chan []byte, 100),
ctx: ctx,
menumanager: menumanager,
}
result.client = dispatcher.RegisterClient(newBridgeClient(result))
@ -88,6 +94,16 @@ func (s *session) start(firstSession bool) {
s.sendMessage("b" + bootstrapMessage)
// Send menus
traymenus, err := s.menumanager.GetTrayMenus()
if err != nil {
s.log.Error(err.Error())
}
for _, trayMenu := range traymenus {
s.sendMessage("TS" + trayMenu)
}
for {
messageType, buffer, err := s.conn.ReadMessage()
if messageType == -1 {

File diff suppressed because one or more lines are too long

View file

@ -2,17 +2,55 @@
export let menu;
export let hidden = true;
</script>
{#if !hidden}
<div class="menu">
{#each menu.Menu.Items as menuItem}
{#if menuItem.Image.length > 0}
<img alt="" src="data:image/png;base64,{menuItem.Image}"/>
{/if}
<span class="menuitem">{menuItem.Label}</span>
{/each}
{#if menu.Menu }
{#each menu.Menu.Items as menuItem}
<div class="menuitem">
{#if menuItem.Image }
<div><img alt="" src="data:image/png;base64,{menuItem.Image}"/></div>
{/if}
<div class="menulabel">{menuItem.Label}</div>
</div>
{/each}
{/if}
</div>
{/if}
<style>
.menu {
padding: 3px;
background-color: #0008;
color: #EEF;
border-radius: 5px;
margin-top: 5px;
position: absolute;
backdrop-filter: blur(3px) saturate(160%) contrast(45%) brightness(140%);
border: 1px solid rgb(88,88,88);
box-shadow: 0 0 1px rgb(146,146,148) inset;
}
.menuitem {
display: flex;
align-items: center;
padding: 1px 5px;
}
.menuitem:hover {
display: flex;
align-items: center;
background-color: rgb(57,131,223);
padding: 1px 5px;
border-radius: 5px;
}
.menuitem img {
padding-right: 5px;
}
</style>

View file

@ -5,6 +5,24 @@
import {trays} from './store'
import TrayMenu from "./TrayMenu.svelte";
import {onMount} from "svelte";
let time = new Date();
$: day = time.toLocaleString("default", { weekday: "short" })
$: dom = time.getDate()
$: mon = time.toLocaleString("default", { month: "short" })
$: currentTime = time.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }).toLowerCase()
$: dateTimeString = `${day} ${dom} ${mon} ${currentTime}`
onMount(() => {
const interval = setInterval(() => {
time = new Date();
}, 1000);
return () => {
clearInterval(interval);
};
});
</script>
@ -12,8 +30,9 @@
<div class="wails-menubar" transition:fade>
<span class="tray-menus">
{#each $trays as tray}
<TrayMenu {tray}></TrayMenu>
<TrayMenu {tray}/>
{/each}
<span class="time">{dateTimeString}</span>
</span>
</div>
{/if}
@ -31,7 +50,12 @@
height: 2rem;
width: 100%;
border-bottom: 1px solid #b3b3b3;
box-shadow: antiquewhite;
box-shadow: 0px 0px 10px 0px #33333360;
box-shadow: 0 0 10px 0 #33333360;
}
.time {
padding-left: 0.5rem;
padding-right: 1.5rem;
overflow: visible;
font-size: 14px;
}
</style>

View file

@ -1,13 +1,52 @@
<script>
import Menu from "./Menu.svelte";
import { selectedMenu } from "./store";
export let tray;
export let tray = null;
$: hidden = $selectedMenu !== tray;
function closeMenu() {
selectedMenu.set(null);
}
function trayClicked() {
if ( $selectedMenu !== tray ) {
selectedMenu.set(tray);
} else {
selectedMenu.set(null);
}
}
// Source: https://svelte.dev/repl/0ace7a508bd843b798ae599940a91783?version=3.16.7
/** Dispatch event on click outside of node */
function clickOutside(node) {
const handleClick = event => {
if (node && !node.contains(event.target) && !event.defaultPrevented) {
node.dispatchEvent(
new CustomEvent('click_outside', node)
)
}
}
document.addEventListener('click', handleClick, true);
return {
destroy() {
document.removeEventListener('click', handleClick, true);
}
}
}
</script>
<span class="tray-menu">
<span class="label">{tray.Label}</span>
<!-- <Menu menu="{tray.ProcessedMenu}"/>-->
<span class="tray-menu" use:clickOutside on:click_outside={closeMenu}>
<!--{#if tray.Image && tray.Image.length > 0}-->
<!-- <img alt="" src="data:image/png;base64,{tray.Image}"/>-->
<!--{/if}-->
<span class="label" on:click={trayClicked}>{tray.Label}</span>
{#if tray.ProcessedMenu }
<Menu menu="{tray.ProcessedMenu}" {hidden}/>
{/if}
</span>
<style>
@ -18,4 +57,9 @@
overflow: visible;
font-size: 14px;
}
.label {
text-align: right;
padding-right: 10px;
}
</style>

View file

@ -29,7 +29,7 @@ export default [
}),
resolve({browser: true}), // so Rollup can find `ms`
// commonjs() // so Rollup can convert `ms` to an ES module
terser(),
// terser(),
]
},

View file

@ -26,7 +26,6 @@ export function hideMenuBar() {
export const trays = writable([]);
export function setTray(tray) {
log("Set Tray:" + JSON.stringify(tray))
trays.update((current) => {
// Remove existing if it exists, else add
const index = current.findIndex(item => item.ID === tray.ID);
@ -39,7 +38,6 @@ export function setTray(tray) {
})
}
export function updateTrayLabel(tray) {
log("Update Tray Label:" + JSON.stringify(tray))
trays.update((current) => {
// Remove existing if it exists, else add
const index = current.findIndex(item => item.ID === tray.ID);
@ -50,3 +48,5 @@ export function updateTrayLabel(tray) {
return current;
})
}
export let selectedMenu = writable(null);