erm/vendor/github.com/lxn/walk/statusbar.go
2021-07-30 23:29:20 +01:00

451 lines
8.2 KiB
Go

// Copyright 2013 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 (
"syscall"
"unsafe"
)
import (
"github.com/lxn/win"
)
// StatusBar is a widget that displays status messages.
type StatusBar struct {
WidgetBase
items *StatusBarItemList
}
// NewStatusBar returns a new StatusBar as child of container parent.
func NewStatusBar(parent Container) (*StatusBar, error) {
sb := new(StatusBar)
if err := InitWidget(
sb,
parent,
"msctls_statusbar32",
win.SBARS_SIZEGRIP|win.SBARS_TOOLTIPS,
0); err != nil {
return nil, err
}
sb.items = newStatusBarItemList(sb)
return sb, nil
}
// Items returns the list of items in the StatusBar.
func (sb *StatusBar) Items() *StatusBarItemList {
return sb.items
}
// SetVisible sets whether the StatusBar is visible.
func (sb *StatusBar) SetVisible(visible bool) {
sb.WidgetBase.SetVisible(visible)
sb.RequestLayout()
}
func (sb *StatusBar) ApplyDPI(dpi int) {
sb.WidgetBase.ApplyDPI(dpi)
sb.update()
}
func (sb *StatusBar) update() error {
if err := sb.updateParts(); err != nil {
return err
}
for i, item := range sb.items.items {
if err := item.update(i); err != nil {
return err
}
}
sb.SetVisible(sb.items.Len() > 0)
return nil
}
func (sb *StatusBar) updateParts() error {
items := sb.items.items
dpi := sb.DPI()
rightEdges := make([]int32, len(items))
var right int32
for i, item := range items {
right += int32(IntFrom96DPI(item.width, dpi))
rightEdges[i] = right
}
var rep *int32
if len(rightEdges) > 0 {
rep = &rightEdges[0]
}
if len(rightEdges) == 1 {
rightEdges[0] = -1
}
if 0 == sb.SendMessage(
win.SB_SETPARTS,
uintptr(len(items)),
uintptr(unsafe.Pointer(rep))) {
return newError("SB_SETPARTS")
}
return nil
}
func (sb *StatusBar) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case win.WM_NOTIFY:
nmhdr := (*win.NMHDR)(unsafe.Pointer(lParam))
switch nmhdr.Code {
case win.NM_CLICK:
lpnm := (*win.NMMOUSE)(unsafe.Pointer(lParam))
if n := int(lpnm.DwItemSpec); n >= 0 && n < sb.items.Len() {
sb.items.At(n).raiseClicked()
}
}
}
return sb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
func (*StatusBar) CreateLayoutItem(ctx *LayoutContext) LayoutItem {
return new(statusBarLayoutItem)
}
type statusBarLayoutItem struct {
LayoutItemBase
}
func (*statusBarLayoutItem) LayoutFlags() LayoutFlags {
return 0
}
func (*statusBarLayoutItem) IdealSize() Size {
return Size{}
}
// StatusBarItem represents a section of a StatusBar that can have its own icon,
// text, tool tip text and width.
type StatusBarItem struct {
sb *StatusBar
icon *Icon
text string
toolTipText string
width int
clickedPublisher EventPublisher
}
// NewStatusBarItem returns a new StatusBarItem.
func NewStatusBarItem() *StatusBarItem {
return &StatusBarItem{width: 100}
}
// Icon returns the Icon of the StatusBarItem.
func (sbi *StatusBarItem) Icon() *Icon {
return sbi.icon
}
// SetIcon sets the Icon of the StatusBarItem.
func (sbi *StatusBarItem) SetIcon(icon *Icon) error {
if icon == sbi.icon {
return nil
}
old := sbi.icon
sbi.icon = icon
return sbi.maybeTry(sbi.updateIcon, func() { sbi.icon = old })
}
// Text returns the text of the StatusBarItem.
func (sbi *StatusBarItem) Text() string {
return sbi.text
}
// SetText sets the text of the StatusBarItem.
func (sbi *StatusBarItem) SetText(text string) error {
if text == sbi.text {
return nil
}
old := sbi.text
sbi.text = text
return sbi.maybeTry(sbi.updateText, func() { sbi.text = old })
}
// ToolTipText returns the tool tip text of the StatusBarItem.
func (sbi *StatusBarItem) ToolTipText() string {
return sbi.toolTipText
}
// SetToolTipText sets the tool tip text of the StatusBarItem.
func (sbi *StatusBarItem) SetToolTipText(toolTipText string) error {
if toolTipText == sbi.toolTipText {
return nil
}
old := sbi.toolTipText
sbi.toolTipText = toolTipText
return sbi.maybeTry(sbi.updateToolTipText, func() { sbi.toolTipText = old })
}
// Width returns the width of the StatusBarItem.
func (sbi *StatusBarItem) Width() int {
return sbi.width
}
// SetWidth sets the width of the StatusBarItem.
func (sbi *StatusBarItem) SetWidth(width int) error {
if width == sbi.width {
return nil
}
old := sbi.width
sbi.width = width
if sbi.sb != nil {
succeeded := false
defer func() {
if !succeeded {
sbi.width = old
}
}()
if err := sbi.sb.updateParts(); err != nil {
return err
}
succeeded = true
}
return nil
}
func (sbi *StatusBarItem) Clicked() *Event {
return sbi.clickedPublisher.Event()
}
func (sbi *StatusBarItem) raiseClicked() {
sbi.clickedPublisher.Publish()
}
func (sbi *StatusBarItem) maybeTry(f func(index int) error, rollback func()) error {
if sbi.sb != nil {
succeeded := false
defer func() {
if !succeeded {
rollback()
}
}()
if err := f(sbi.sb.items.Index(sbi)); err != nil {
return err
}
succeeded = true
}
return nil
}
func (sbi *StatusBarItem) update(index int) error {
if err := sbi.updateIcon(index); err != nil {
return err
}
if err := sbi.updateText(index); err != nil {
return err
}
if err := sbi.updateToolTipText(index); err != nil {
return err
}
return nil
}
func (sbi *StatusBarItem) updateIcon(index int) error {
var hIcon win.HICON
if sbi.icon != nil {
hIcon = sbi.icon.handleForDPI(sbi.sb.DPI())
}
if 0 == sbi.sb.SendMessage(
win.SB_SETICON,
uintptr(index),
uintptr(hIcon)) {
return newError("SB_SETICON")
}
return nil
}
func (sbi *StatusBarItem) updateText(index int) error {
utf16, err := syscall.UTF16PtrFromString(sbi.text)
if err != nil {
return err
}
if 0 == sbi.sb.SendMessage(
win.SB_SETTEXT,
uintptr(win.MAKEWORD(byte(index), 0)),
uintptr(unsafe.Pointer(utf16))) {
return newError("SB_SETTEXT")
}
return nil
}
func (sbi *StatusBarItem) updateToolTipText(index int) error {
utf16, err := syscall.UTF16PtrFromString(sbi.toolTipText)
if err != nil {
return err
}
sbi.sb.SendMessage(
win.SB_SETTIPTEXT,
uintptr(index),
uintptr(unsafe.Pointer(utf16)))
return nil
}
type StatusBarItemList struct {
sb *StatusBar
items []*StatusBarItem
}
func newStatusBarItemList(statusBar *StatusBar) *StatusBarItemList {
return &StatusBarItemList{sb: statusBar}
}
func (l *StatusBarItemList) Add(item *StatusBarItem) error {
return l.Insert(len(l.items), item)
}
func (l *StatusBarItemList) At(index int) *StatusBarItem {
return l.items[index]
}
func (l *StatusBarItemList) Clear() error {
old := l.items
l.items = l.items[:0]
succeeded := false
defer func() {
if !succeeded {
l.items = old
l.sb.update()
}
}()
if err := l.sb.update(); err != nil {
return err
}
succeeded = true
return nil
}
func (l *StatusBarItemList) Index(item *StatusBarItem) int {
for i, it := range l.items {
if it == item {
return i
}
}
return -1
}
func (l *StatusBarItemList) Contains(item *StatusBarItem) bool {
return l.Index(item) > -1
}
func (l *StatusBarItemList) Insert(index int, item *StatusBarItem) error {
if item.sb != nil {
return newError("item already contained in a StatusBar")
}
l.items = append(l.items, nil)
copy(l.items[index+1:], l.items[index:])
l.items[index] = item
item.sb = l.sb
succeeded := false
defer func() {
if !succeeded {
item.sb = nil
l.items = append(l.items[:index], l.items[index+1:]...)
l.sb.update()
}
}()
if err := l.sb.update(); err != nil {
return err
}
succeeded = true
return nil
}
func (l *StatusBarItemList) Len() int {
return len(l.items)
}
func (l *StatusBarItemList) Remove(item *StatusBarItem) error {
index := l.Index(item)
if index == -1 {
return nil
}
return l.RemoveAt(index)
}
func (l *StatusBarItemList) RemoveAt(index int) error {
item := l.items[index]
item.sb = nil
l.items = append(l.items[:index], l.items[index+1:]...)
succeeded := false
defer func() {
if !succeeded {
l.items = append(l.items, nil)
copy(l.items[index+1:], l.items[index:])
l.items[index] = item
item.sb = l.sb
l.sb.update()
}
}()
if err := l.sb.update(); err != nil {
return err
}
succeeded = true
return nil
}