261 lines
5.0 KiB
Go
261 lines
5.0 KiB
Go
// Copyright 2010 The Walk Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build windows
|
|
|
|
package walk
|
|
|
|
import (
|
|
"unsafe"
|
|
|
|
"github.com/lxn/win"
|
|
)
|
|
|
|
const (
|
|
DlgCmdNone = 0
|
|
DlgCmdOK = win.IDOK
|
|
DlgCmdCancel = win.IDCANCEL
|
|
DlgCmdAbort = win.IDABORT
|
|
DlgCmdRetry = win.IDRETRY
|
|
DlgCmdIgnore = win.IDIGNORE
|
|
DlgCmdYes = win.IDYES
|
|
DlgCmdNo = win.IDNO
|
|
DlgCmdClose = win.IDCLOSE
|
|
DlgCmdHelp = win.IDHELP
|
|
DlgCmdTryAgain = win.IDTRYAGAIN
|
|
DlgCmdContinue = win.IDCONTINUE
|
|
DlgCmdTimeout = win.IDTIMEOUT
|
|
)
|
|
|
|
const dialogWindowClass = `\o/ Walk_Dialog_Class \o/`
|
|
|
|
func init() {
|
|
AppendToWalkInit(func() {
|
|
MustRegisterWindowClass(dialogWindowClass)
|
|
})
|
|
}
|
|
|
|
type dialogish interface {
|
|
DefaultButton() *PushButton
|
|
CancelButton() *PushButton
|
|
}
|
|
|
|
type Dialog struct {
|
|
FormBase
|
|
result int
|
|
defaultButton *PushButton
|
|
cancelButton *PushButton
|
|
centerInOwnerWhenRun bool
|
|
}
|
|
|
|
func NewDialog(owner Form) (*Dialog, error) {
|
|
return newDialogWithStyle(owner, win.WS_THICKFRAME)
|
|
}
|
|
|
|
func NewDialogWithFixedSize(owner Form) (*Dialog, error) {
|
|
return newDialogWithStyle(owner, 0)
|
|
}
|
|
|
|
func newDialogWithStyle(owner Form, style uint32) (*Dialog, error) {
|
|
dlg := &Dialog{
|
|
FormBase: FormBase{
|
|
owner: owner,
|
|
},
|
|
}
|
|
|
|
if err := InitWindow(
|
|
dlg,
|
|
owner,
|
|
dialogWindowClass,
|
|
win.WS_CAPTION|win.WS_SYSMENU|style,
|
|
0); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
succeeded := false
|
|
defer func() {
|
|
if !succeeded {
|
|
dlg.Dispose()
|
|
}
|
|
}()
|
|
|
|
dlg.centerInOwnerWhenRun = owner != nil
|
|
|
|
dlg.result = DlgCmdNone
|
|
|
|
succeeded = true
|
|
|
|
return dlg, nil
|
|
}
|
|
|
|
func (dlg *Dialog) DefaultButton() *PushButton {
|
|
return dlg.defaultButton
|
|
}
|
|
|
|
func (dlg *Dialog) SetDefaultButton(button *PushButton) error {
|
|
if button != nil && !win.IsChild(dlg.hWnd, button.hWnd) {
|
|
return newError("not a descendant of the dialog")
|
|
}
|
|
|
|
succeeded := false
|
|
if dlg.defaultButton != nil {
|
|
if err := dlg.defaultButton.setAndClearStyleBits(win.BS_PUSHBUTTON, win.BS_DEFPUSHBUTTON); err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if !succeeded {
|
|
dlg.defaultButton.setAndClearStyleBits(win.BS_DEFPUSHBUTTON, win.BS_PUSHBUTTON)
|
|
}
|
|
}()
|
|
}
|
|
|
|
if button != nil {
|
|
if err := button.setAndClearStyleBits(win.BS_DEFPUSHBUTTON, win.BS_PUSHBUTTON); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
dlg.defaultButton = button
|
|
|
|
succeeded = true
|
|
|
|
return nil
|
|
}
|
|
|
|
func (dlg *Dialog) CancelButton() *PushButton {
|
|
return dlg.cancelButton
|
|
}
|
|
|
|
func (dlg *Dialog) SetCancelButton(button *PushButton) error {
|
|
if button != nil && !win.IsChild(dlg.hWnd, button.hWnd) {
|
|
return newError("not a descendant of the dialog")
|
|
}
|
|
|
|
dlg.cancelButton = button
|
|
|
|
return nil
|
|
}
|
|
|
|
func (dlg *Dialog) Result() int {
|
|
return dlg.result
|
|
}
|
|
|
|
func (dlg *Dialog) Accept() {
|
|
dlg.Close(DlgCmdOK)
|
|
}
|
|
|
|
func (dlg *Dialog) Cancel() {
|
|
dlg.Close(DlgCmdCancel)
|
|
}
|
|
|
|
func (dlg *Dialog) Close(result int) {
|
|
dlg.result = result
|
|
|
|
dlg.FormBase.Close()
|
|
}
|
|
|
|
func (dlg *Dialog) Show() {
|
|
var willRestore bool
|
|
if dlg.Persistent() {
|
|
state, _ := dlg.ReadState()
|
|
willRestore = state != ""
|
|
}
|
|
|
|
if !willRestore {
|
|
var size Size
|
|
if layout := dlg.Layout(); layout != nil {
|
|
size = maxSize(dlg.clientComposite.MinSizeHint(), dlg.MinSizePixels())
|
|
} else {
|
|
size = dlg.SizePixels()
|
|
}
|
|
|
|
if dlg.owner != nil {
|
|
ob := dlg.owner.BoundsPixels()
|
|
|
|
if dlg.centerInOwnerWhenRun {
|
|
dlg.SetBoundsPixels(fitRectToScreen(dlg.hWnd, Rectangle{
|
|
ob.X + (ob.Width-size.Width)/2,
|
|
ob.Y + (ob.Height-size.Height)/2,
|
|
size.Width,
|
|
size.Height,
|
|
}))
|
|
}
|
|
} else {
|
|
b := dlg.BoundsPixels()
|
|
|
|
dlg.SetBoundsPixels(Rectangle{b.X, b.Y, size.Width, size.Height})
|
|
}
|
|
}
|
|
|
|
dlg.FormBase.Show()
|
|
|
|
dlg.startLayout()
|
|
}
|
|
|
|
// fitRectToScreen fits rectangle to screen. Input and output rectangles are in native pixels.
|
|
func fitRectToScreen(hWnd win.HWND, r Rectangle) Rectangle {
|
|
var mi win.MONITORINFO
|
|
mi.CbSize = uint32(unsafe.Sizeof(mi))
|
|
|
|
if !win.GetMonitorInfo(win.MonitorFromWindow(
|
|
hWnd, win.MONITOR_DEFAULTTOPRIMARY), &mi) {
|
|
|
|
return r
|
|
}
|
|
|
|
mon := rectangleFromRECT(mi.RcWork)
|
|
|
|
dpi := win.GetDpiForWindow(hWnd)
|
|
mon.Height -= int(win.GetSystemMetricsForDpi(win.SM_CYCAPTION, dpi))
|
|
|
|
if r.Width <= mon.Width {
|
|
switch {
|
|
case r.X < mon.X:
|
|
r.X = mon.X
|
|
case r.X+r.Width > mon.X+mon.Width:
|
|
r.X = mon.X + mon.Width - r.Width
|
|
}
|
|
}
|
|
|
|
if r.Height <= mon.Height {
|
|
switch {
|
|
case r.Y < mon.Y:
|
|
r.Y = mon.Y
|
|
case r.Y+r.Height > mon.Y+mon.Height:
|
|
r.Y = mon.Y + mon.Height - r.Height
|
|
}
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
func (dlg *Dialog) Run() int {
|
|
dlg.Show()
|
|
|
|
dlg.FormBase.Run()
|
|
|
|
return dlg.result
|
|
}
|
|
|
|
func (dlg *Dialog) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
|
|
switch msg {
|
|
case win.WM_COMMAND:
|
|
if win.HIWORD(uint32(wParam)) == 0 {
|
|
switch win.LOWORD(uint32(wParam)) {
|
|
case DlgCmdOK:
|
|
if dlg.defaultButton != nil {
|
|
dlg.defaultButton.raiseClicked()
|
|
}
|
|
|
|
case DlgCmdCancel:
|
|
if dlg.cancelButton != nil {
|
|
dlg.cancelButton.raiseClicked()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return dlg.FormBase.WndProc(hwnd, msg, wParam, lParam)
|
|
}
|