fix(display): correct GetWindowTitle and add WS input validation
GetWindowTitle was returning info.Name (the window's identifier) instead of the actual title. Added Title() to the PlatformWindow interface and Title field to WindowInfo so the real title flows through queries. Added wsRequire() helper and input validation for all webview:* WS message cases — window name is required for every webview action, and selectors/URLs are validated where they'd cause errors if empty. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ed44b6f7f9
commit
f884d698b2
7 changed files with 319 additions and 35 deletions
|
|
@ -237,6 +237,15 @@ type WSMessage struct {
|
|||
Data map[string]any `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// wsRequire extracts a string field from WS data and returns an error if it is empty.
|
||||
func wsRequire(data map[string]any, key string) (string, error) {
|
||||
v, _ := data[key].(string)
|
||||
if v == "" {
|
||||
return "", fmt.Errorf("ws: missing required field %q", key)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// handleWSMessage bridges WebSocket commands to IPC calls.
|
||||
func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) {
|
||||
var result any
|
||||
|
|
@ -291,47 +300,98 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) {
|
|||
case "contextmenu:list":
|
||||
result, handled, err = s.Core().QUERY(contextmenu.QueryList{})
|
||||
case "webview:eval":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
script, _ := msg.Data["script"].(string)
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskEvaluate{Window: w, Script: script})
|
||||
case "webview:click":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskClick{Window: w, Selector: sel})
|
||||
case "webview:type":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
text, _ := msg.Data["text"].(string)
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskType{Window: w, Selector: sel, Text: text})
|
||||
case "webview:navigate":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
url, _ := msg.Data["url"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
url, e := wsRequire(msg.Data, "url")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskNavigate{Window: w, URL: url})
|
||||
case "webview:screenshot":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskScreenshot{Window: w})
|
||||
case "webview:scroll":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
x, _ := msg.Data["x"].(float64)
|
||||
y, _ := msg.Data["y"].(float64)
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskScroll{Window: w, X: int(x), Y: int(y)})
|
||||
case "webview:hover":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskHover{Window: w, Selector: sel})
|
||||
case "webview:select":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
val, _ := msg.Data["value"].(string)
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskSelect{Window: w, Selector: sel, Value: val})
|
||||
case "webview:check":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
checked, _ := msg.Data["checked"].(bool)
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskCheck{Window: w, Selector: sel, Checked: checked})
|
||||
case "webview:upload":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
pathsRaw, _ := msg.Data["paths"].([]any)
|
||||
var paths []string
|
||||
for _, p := range pathsRaw {
|
||||
|
|
@ -341,15 +401,24 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) {
|
|||
}
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskUploadFile{Window: w, Selector: sel, Paths: paths})
|
||||
case "webview:viewport":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
width, _ := msg.Data["width"].(float64)
|
||||
height, _ := msg.Data["height"].(float64)
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskSetViewport{Window: w, Width: int(width), Height: int(height)})
|
||||
case "webview:clear-console":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().PERFORM(webview.TaskClearConsole{Window: w})
|
||||
case "webview:console":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
level, _ := msg.Data["level"].(string)
|
||||
limit := 100
|
||||
if l, ok := msg.Data["limit"].(float64); ok {
|
||||
|
|
@ -357,22 +426,43 @@ func (s *Service) handleWSMessage(msg WSMessage) (any, bool, error) {
|
|||
}
|
||||
result, handled, err = s.Core().QUERY(webview.QueryConsole{Window: w, Level: level, Limit: limit})
|
||||
case "webview:query":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().QUERY(webview.QuerySelector{Window: w, Selector: sel})
|
||||
case "webview:query-all":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, e := wsRequire(msg.Data, "selector")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().QUERY(webview.QuerySelectorAll{Window: w, Selector: sel})
|
||||
case "webview:dom-tree":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
sel, _ := msg.Data["selector"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
sel, _ := msg.Data["selector"].(string) // selector optional for dom-tree (defaults to root)
|
||||
result, handled, err = s.Core().QUERY(webview.QueryDOMTree{Window: w, Selector: sel})
|
||||
case "webview:url":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().QUERY(webview.QueryURL{Window: w})
|
||||
case "webview:title":
|
||||
w, _ := msg.Data["window"].(string)
|
||||
w, e := wsRequire(msg.Data, "window")
|
||||
if e != nil {
|
||||
return nil, false, e
|
||||
}
|
||||
result, handled, err = s.Core().QUERY(webview.QueryTitle{Window: w})
|
||||
default:
|
||||
return nil, false, nil
|
||||
|
|
@ -685,7 +775,7 @@ func (s *Service) GetWindowTitle(name string) (string, error) {
|
|||
if info == nil {
|
||||
return "", fmt.Errorf("window not found: %s", name)
|
||||
}
|
||||
return info.Name, nil // Wails v3 doesn't expose a title getter
|
||||
return info.Title, nil
|
||||
}
|
||||
|
||||
// ResetWindowState clears saved window positions.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package window
|
|||
// WindowInfo contains information about a window.
|
||||
type WindowInfo struct {
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
X int `json:"x"`
|
||||
Y int `json:"y"`
|
||||
Width int `json:"width"`
|
||||
|
|
@ -52,6 +53,58 @@ type TaskMinimise struct{ Name string }
|
|||
// TaskFocus brings a window to the front.
|
||||
type TaskFocus struct{ Name string }
|
||||
|
||||
// TaskRestore restores a maximised or minimised window to its normal state.
|
||||
type TaskRestore struct{ Name string }
|
||||
|
||||
// TaskSetTitle changes a window's title.
|
||||
type TaskSetTitle struct {
|
||||
Name string
|
||||
Title string
|
||||
}
|
||||
|
||||
// TaskSetVisibility shows or hides a window.
|
||||
type TaskSetVisibility struct {
|
||||
Name string
|
||||
Visible bool
|
||||
}
|
||||
|
||||
// TaskFullscreen enters or exits fullscreen mode.
|
||||
type TaskFullscreen struct {
|
||||
Name string
|
||||
Fullscreen bool
|
||||
}
|
||||
|
||||
// --- Layout Queries ---
|
||||
|
||||
// QueryLayoutList returns summaries of all saved layouts. Result: []LayoutInfo
|
||||
type QueryLayoutList struct{}
|
||||
|
||||
// QueryLayoutGet returns a layout by name. Result: *Layout (nil if not found)
|
||||
type QueryLayoutGet struct{ Name string }
|
||||
|
||||
// --- Layout Tasks ---
|
||||
|
||||
// TaskSaveLayout saves the current window arrangement as a named layout. Result: bool
|
||||
type TaskSaveLayout struct{ Name string }
|
||||
|
||||
// TaskRestoreLayout restores a saved layout by name.
|
||||
type TaskRestoreLayout struct{ Name string }
|
||||
|
||||
// TaskDeleteLayout removes a saved layout by name.
|
||||
type TaskDeleteLayout struct{ Name string }
|
||||
|
||||
// TaskTileWindows arranges windows in a tiling mode.
|
||||
type TaskTileWindows struct {
|
||||
Mode string // "left-right", "grid", "left-half", "right-half", etc.
|
||||
Windows []string // window names; empty = all
|
||||
}
|
||||
|
||||
// TaskSnapWindow snaps a window to a screen edge/corner.
|
||||
type TaskSnapWindow struct {
|
||||
Name string // window name
|
||||
Position string // "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right", "center"
|
||||
}
|
||||
|
||||
// TaskSaveConfig persists this service's config section via the display orchestrator.
|
||||
type TaskSaveConfig struct{ Value map[string]any }
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ type MockWindow struct {
|
|||
}
|
||||
|
||||
func (w *MockWindow) Name() string { return w.name }
|
||||
func (w *MockWindow) Title() string { return w.title }
|
||||
func (w *MockWindow) Position() (int, int) { return w.x, w.y }
|
||||
func (w *MockWindow) Size() (int, int) { return w.width, w.height }
|
||||
func (w *MockWindow) IsMaximised() bool { return w.maximised }
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ type mockWindow struct {
|
|||
}
|
||||
|
||||
func (w *mockWindow) Name() string { return w.name }
|
||||
func (w *mockWindow) Title() string { return w.title }
|
||||
func (w *mockWindow) Position() (int, int) { return w.x, w.y }
|
||||
func (w *mockWindow) Size() (int, int) { return w.width, w.height }
|
||||
func (w *mockWindow) IsMaximised() bool { return w.maximised }
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ type PlatformWindowOptions struct {
|
|||
type PlatformWindow interface {
|
||||
// Identity
|
||||
Name() string
|
||||
Title() string
|
||||
|
||||
// Queries
|
||||
Position() (int, int)
|
||||
|
|
|
|||
|
|
@ -68,6 +68,14 @@ func (s *Service) handleQuery(c *core.Core, q core.Query) (any, bool, error) {
|
|||
return s.queryWindowList(), true, nil
|
||||
case QueryWindowByName:
|
||||
return s.queryWindowByName(q.Name), true, nil
|
||||
case QueryLayoutList:
|
||||
return s.manager.Layout().ListLayouts(), true, nil
|
||||
case QueryLayoutGet:
|
||||
l, ok := s.manager.Layout().GetLayout(q.Name)
|
||||
if !ok {
|
||||
return (*Layout)(nil), true, nil
|
||||
}
|
||||
return &l, true, nil
|
||||
default:
|
||||
return nil, false, nil
|
||||
}
|
||||
|
|
@ -81,7 +89,7 @@ func (s *Service) queryWindowList() []WindowInfo {
|
|||
x, y := pw.Position()
|
||||
w, h := pw.Size()
|
||||
result = append(result, WindowInfo{
|
||||
Name: name, X: x, Y: y, Width: w, Height: h,
|
||||
Name: name, Title: pw.Title(), X: x, Y: y, Width: w, Height: h,
|
||||
Maximized: pw.IsMaximised(),
|
||||
Focused: pw.IsFocused(),
|
||||
})
|
||||
|
|
@ -98,7 +106,7 @@ func (s *Service) queryWindowByName(name string) *WindowInfo {
|
|||
x, y := pw.Position()
|
||||
w, h := pw.Size()
|
||||
return &WindowInfo{
|
||||
Name: name, X: x, Y: y, Width: w, Height: h,
|
||||
Name: name, Title: pw.Title(), X: x, Y: y, Width: w, Height: h,
|
||||
Maximized: pw.IsMaximised(),
|
||||
Focused: pw.IsFocused(),
|
||||
}
|
||||
|
|
@ -122,6 +130,25 @@ func (s *Service) handleTask(c *core.Core, t core.Task) (any, bool, error) {
|
|||
return nil, true, s.taskMinimise(t.Name)
|
||||
case TaskFocus:
|
||||
return nil, true, s.taskFocus(t.Name)
|
||||
case TaskRestore:
|
||||
return nil, true, s.taskRestore(t.Name)
|
||||
case TaskSetTitle:
|
||||
return nil, true, s.taskSetTitle(t.Name, t.Title)
|
||||
case TaskSetVisibility:
|
||||
return nil, true, s.taskSetVisibility(t.Name, t.Visible)
|
||||
case TaskFullscreen:
|
||||
return nil, true, s.taskFullscreen(t.Name, t.Fullscreen)
|
||||
case TaskSaveLayout:
|
||||
return nil, true, s.taskSaveLayout(t.Name)
|
||||
case TaskRestoreLayout:
|
||||
return nil, true, s.taskRestoreLayout(t.Name)
|
||||
case TaskDeleteLayout:
|
||||
s.manager.Layout().DeleteLayout(t.Name)
|
||||
return nil, true, nil
|
||||
case TaskTileWindows:
|
||||
return nil, true, s.taskTileWindows(t.Mode, t.Windows)
|
||||
case TaskSnapWindow:
|
||||
return nil, true, s.taskSnapWindow(t.Name, t.Position)
|
||||
default:
|
||||
return nil, false, nil
|
||||
}
|
||||
|
|
@ -134,7 +161,7 @@ func (s *Service) taskOpenWindow(t TaskOpenWindow) (any, bool, error) {
|
|||
}
|
||||
x, y := pw.Position()
|
||||
w, h := pw.Size()
|
||||
info := WindowInfo{Name: pw.Name(), X: x, Y: y, Width: w, Height: h}
|
||||
info := WindowInfo{Name: pw.Name(), Title: pw.Title(), X: x, Y: y, Width: w, Height: h}
|
||||
|
||||
// Attach platform event listeners that convert to IPC actions
|
||||
s.trackWindow(pw)
|
||||
|
|
@ -238,6 +265,114 @@ func (s *Service) taskFocus(name string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskRestore(name string) error {
|
||||
pw, ok := s.manager.Get(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("window not found: %s", name)
|
||||
}
|
||||
pw.Restore()
|
||||
s.manager.State().UpdateMaximized(name, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskSetTitle(name, title string) error {
|
||||
pw, ok := s.manager.Get(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("window not found: %s", name)
|
||||
}
|
||||
pw.SetTitle(title)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskSetVisibility(name string, visible bool) error {
|
||||
pw, ok := s.manager.Get(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("window not found: %s", name)
|
||||
}
|
||||
pw.SetVisibility(visible)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskFullscreen(name string, fullscreen bool) error {
|
||||
pw, ok := s.manager.Get(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("window not found: %s", name)
|
||||
}
|
||||
if fullscreen {
|
||||
pw.Fullscreen()
|
||||
} else {
|
||||
pw.UnFullscreen()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) taskSaveLayout(name string) error {
|
||||
windows := s.queryWindowList()
|
||||
states := make(map[string]WindowState, len(windows))
|
||||
for _, w := range windows {
|
||||
states[w.Name] = WindowState{
|
||||
X: w.X, Y: w.Y, Width: w.Width, Height: w.Height,
|
||||
Maximized: w.Maximized,
|
||||
}
|
||||
}
|
||||
return s.manager.Layout().SaveLayout(name, states)
|
||||
}
|
||||
|
||||
func (s *Service) taskRestoreLayout(name string) error {
|
||||
layout, ok := s.manager.Layout().GetLayout(name)
|
||||
if !ok {
|
||||
return fmt.Errorf("layout not found: %s", name)
|
||||
}
|
||||
for winName, state := range layout.Windows {
|
||||
pw, found := s.manager.Get(winName)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
pw.SetPosition(state.X, state.Y)
|
||||
pw.SetSize(state.Width, state.Height)
|
||||
if state.Maximized {
|
||||
pw.Maximise()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var tileModeMap = map[string]TileMode{
|
||||
"left-half": TileModeLeftHalf, "right-half": TileModeRightHalf,
|
||||
"top-half": TileModeTopHalf, "bottom-half": TileModeBottomHalf,
|
||||
"top-left": TileModeTopLeft, "top-right": TileModeTopRight,
|
||||
"bottom-left": TileModeBottomLeft, "bottom-right": TileModeBottomRight,
|
||||
"left-right": TileModeLeftRight, "grid": TileModeGrid,
|
||||
}
|
||||
|
||||
func (s *Service) taskTileWindows(mode string, names []string) error {
|
||||
tm, ok := tileModeMap[mode]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown tile mode: %s", mode)
|
||||
}
|
||||
if len(names) == 0 {
|
||||
names = s.manager.List()
|
||||
}
|
||||
// Default screen size — callers can query screen_primary for actual values.
|
||||
return s.manager.TileWindows(tm, names, 1920, 1080)
|
||||
}
|
||||
|
||||
var snapPosMap = map[string]SnapPosition{
|
||||
"left": SnapLeft, "right": SnapRight,
|
||||
"top": SnapTop, "bottom": SnapBottom,
|
||||
"top-left": SnapTopLeft, "top-right": SnapTopRight,
|
||||
"bottom-left": SnapBottomLeft, "bottom-right": SnapBottomRight,
|
||||
"center": SnapCenter, "centre": SnapCenter,
|
||||
}
|
||||
|
||||
func (s *Service) taskSnapWindow(name, position string) error {
|
||||
pos, ok := snapPosMap[position]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown snap position: %s", position)
|
||||
}
|
||||
return s.manager.SnapWindow(name, pos, 1920, 1080)
|
||||
}
|
||||
|
||||
// Manager returns the underlying window Manager for direct access.
|
||||
func (s *Service) Manager() *Manager {
|
||||
return s.manager
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func (wp *WailsPlatform) CreateWindow(opts PlatformWindowOptions) PlatformWindow
|
|||
BackgroundColour: application.NewRGBA(opts.BackgroundColour[0], opts.BackgroundColour[1], opts.BackgroundColour[2], opts.BackgroundColour[3]),
|
||||
}
|
||||
w := wp.app.Window.NewWithOptions(wOpts)
|
||||
return &wailsWindow{w: w}
|
||||
return &wailsWindow{w: w, title: opts.Title}
|
||||
}
|
||||
|
||||
func (wp *WailsPlatform) GetWindows() []PlatformWindow {
|
||||
|
|
@ -52,16 +52,19 @@ func (wp *WailsPlatform) GetWindows() []PlatformWindow {
|
|||
}
|
||||
|
||||
// wailsWindow wraps *application.WebviewWindow to implement PlatformWindow.
|
||||
// It stores the title locally because Wails v3 does not expose a title getter.
|
||||
type wailsWindow struct {
|
||||
w *application.WebviewWindow
|
||||
w *application.WebviewWindow
|
||||
title string
|
||||
}
|
||||
|
||||
func (ww *wailsWindow) Name() string { return ww.w.Name() }
|
||||
func (ww *wailsWindow) Title() string { return ww.title }
|
||||
func (ww *wailsWindow) Position() (int, int) { return ww.w.Position() }
|
||||
func (ww *wailsWindow) Size() (int, int) { return ww.w.Size() }
|
||||
func (ww *wailsWindow) IsMaximised() bool { return ww.w.IsMaximised() }
|
||||
func (ww *wailsWindow) IsFocused() bool { return ww.w.IsFocused() }
|
||||
func (ww *wailsWindow) SetTitle(title string) { ww.w.SetTitle(title) }
|
||||
func (ww *wailsWindow) SetTitle(title string) { ww.title = title; ww.w.SetTitle(title) }
|
||||
func (ww *wailsWindow) SetPosition(x, y int) { ww.w.SetPosition(x, y) }
|
||||
func (ww *wailsWindow) SetSize(width, height int) { ww.w.SetSize(width, height) }
|
||||
func (ww *wailsWindow) SetBackgroundColour(r, g, b, a uint8) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue