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

702 lines
20 KiB
Go

// Copyright 2012 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"
"github.com/lxn/win"
)
// BindingValueProvider is the interface that a model must implement to support
// data binding with widgets like ComboBox.
type BindingValueProvider interface {
BindingValue(index int) interface{}
}
// ListModel is the interface that a model must implement to support widgets
// like ComboBox.
type ListModel interface {
// ItemCount returns the number of items in the model.
ItemCount() int
// Value returns the value that should be displayed for the given index.
Value(index int) interface{}
// ItemsReset returns the event that the model should publish when the
// number of its items changes.
ItemsReset() *Event
// ItemChanged returns the event that the model should publish when an item
// was changed.
ItemChanged() *IntEvent
// ItemsInserted returns the event that the model should publish when a
// contiguous range of items was inserted.
ItemsInserted() *IntRangeEvent
// ItemsRemoved returns the event that the model should publish when a
// contiguous range of items was removed.
ItemsRemoved() *IntRangeEvent
}
// ListModelBase implements the ItemsReset and ItemChanged methods of the
// ListModel interface.
type ListModelBase struct {
itemsResetPublisher EventPublisher
itemChangedPublisher IntEventPublisher
itemsInsertedPublisher IntRangeEventPublisher
itemsRemovedPublisher IntRangeEventPublisher
}
func (lmb *ListModelBase) ItemsReset() *Event {
return lmb.itemsResetPublisher.Event()
}
func (lmb *ListModelBase) ItemChanged() *IntEvent {
return lmb.itemChangedPublisher.Event()
}
func (lmb *ListModelBase) ItemsInserted() *IntRangeEvent {
return lmb.itemsInsertedPublisher.Event()
}
func (lmb *ListModelBase) ItemsRemoved() *IntRangeEvent {
return lmb.itemsRemovedPublisher.Event()
}
func (lmb *ListModelBase) PublishItemsReset() {
lmb.itemsResetPublisher.Publish()
}
func (lmb *ListModelBase) PublishItemChanged(index int) {
lmb.itemChangedPublisher.Publish(index)
}
func (lmb *ListModelBase) PublishItemsInserted(from, to int) {
lmb.itemsInsertedPublisher.Publish(from, to)
}
func (lmb *ListModelBase) PublishItemsRemoved(from, to int) {
lmb.itemsRemovedPublisher.Publish(from, to)
}
// ReflectListModel provides an alternative to the ListModel interface. It
// uses reflection to obtain data.
type ReflectListModel interface {
// Items returns the model data, which must be a slice of pointer to struct.
Items() interface{}
// ItemsReset returns the event that the model should publish when the
// number of its items changes.
ItemsReset() *Event
// ItemChanged returns the event that the model should publish when an item
// was changed.
ItemChanged() *IntEvent
// ItemsInserted returns the event that the model should publish when a
// contiguous range of items was inserted.
ItemsInserted() *IntRangeEvent
// ItemsRemoved returns the event that the model should publish when a
// contiguous range of items was removed.
ItemsRemoved() *IntRangeEvent
setValueFunc(value func(index int) interface{})
}
// ReflectListModelBase implements the ItemsReset and ItemChanged methods of
// the ReflectListModel interface.
type ReflectListModelBase struct {
ListModelBase
value func(index int) interface{}
}
func (rlmb *ReflectListModelBase) setValueFunc(value func(index int) interface{}) {
rlmb.value = value
}
func (rlmb *ReflectListModelBase) Value(index int) interface{} {
return rlmb.value(index)
}
// TableModel is the interface that a model must implement to support widgets
// like TableView.
type TableModel interface {
// RowCount returns the number of rows in the model.
RowCount() int
// Value returns the value that should be displayed for the given cell.
Value(row, col int) interface{}
// RowsReset returns the event that the model should publish when the number
// of its rows changes.
RowsReset() *Event
// RowChanged returns the event that the model should publish when a row was
// changed.
RowChanged() *IntEvent
// RowsChanged returns the event that the model should publish when a
// contiguous range of items was changed.
RowsChanged() *IntRangeEvent
// RowsInserted returns the event that the model should publish when a
// contiguous range of items was inserted. If the model supports sorting, it
// is assumed to be sorted before the model publishes the event.
RowsInserted() *IntRangeEvent
// RowsRemoved returns the event that the model should publish when a
// contiguous range of items was removed.
RowsRemoved() *IntRangeEvent
}
// TableModelBase implements the RowsReset and RowChanged methods of the
// TableModel interface.
type TableModelBase struct {
rowsResetPublisher EventPublisher
rowChangedPublisher IntEventPublisher
rowsChangedPublisher IntRangeEventPublisher
rowsInsertedPublisher IntRangeEventPublisher
rowsRemovedPublisher IntRangeEventPublisher
}
func (tmb *TableModelBase) RowsReset() *Event {
return tmb.rowsResetPublisher.Event()
}
func (tmb *TableModelBase) RowChanged() *IntEvent {
return tmb.rowChangedPublisher.Event()
}
func (tmb *TableModelBase) RowsChanged() *IntRangeEvent {
return tmb.rowsChangedPublisher.Event()
}
func (tmb *TableModelBase) RowsInserted() *IntRangeEvent {
return tmb.rowsInsertedPublisher.Event()
}
func (tmb *TableModelBase) RowsRemoved() *IntRangeEvent {
return tmb.rowsRemovedPublisher.Event()
}
func (tmb *TableModelBase) PublishRowsReset() {
tmb.rowsResetPublisher.Publish()
}
func (tmb *TableModelBase) PublishRowChanged(row int) {
tmb.rowChangedPublisher.Publish(row)
}
func (tmb *TableModelBase) PublishRowsChanged(from, to int) {
tmb.rowsChangedPublisher.Publish(from, to)
}
func (tmb *TableModelBase) PublishRowsInserted(from, to int) {
tmb.rowsInsertedPublisher.Publish(from, to)
}
func (tmb *TableModelBase) PublishRowsRemoved(from, to int) {
tmb.rowsRemovedPublisher.Publish(from, to)
}
// ReflectTableModel provides an alternative to the TableModel interface. It
// uses reflection to obtain data.
type ReflectTableModel interface {
// Items returns the model data, which must be a slice of pointer to struct.
Items() interface{}
// RowsReset returns the event that the model should publish when the
// number of its items changes.
RowsReset() *Event
// RowChanged returns the event that the model should publish when an item
// was changed.
RowChanged() *IntEvent
// RowsChanged returns the event that the model should publish when a
// contiguous range of items was changed.
RowsChanged() *IntRangeEvent
// RowsInserted returns the event that the model should publish when a
// contiguous range of items was inserted. If the model supports sorting, it
// is assumed to be sorted before the model publishes the event.
RowsInserted() *IntRangeEvent
// RowsRemoved returns the event that the model should publish when a
// contiguous range of items was removed.
RowsRemoved() *IntRangeEvent
setValueFunc(value func(row, col int) interface{})
}
// ReflectTableModelBase implements the ItemsReset and ItemChanged methods of
// the ReflectTableModel interface.
type ReflectTableModelBase struct {
TableModelBase
value func(row, col int) interface{}
}
func (rtmb *ReflectTableModelBase) setValueFunc(value func(row, col int) interface{}) {
rtmb.value = value
}
func (rtmb *ReflectTableModelBase) Value(row, col int) interface{} {
return rtmb.value(row, col)
}
type interceptedSorter interface {
sorterBase() *SorterBase
setSortFunc(sort func(col int, order SortOrder) error)
}
// SortedReflectTableModelBase implements the RowsReset and RowChanged methods
// of the ReflectTableModel interface as well as the Sorter interface for
// pre-implemented in-memory sorting.
type SortedReflectTableModelBase struct {
ReflectTableModelBase
SorterBase
sort func(col int, order SortOrder) error
}
func (srtmb *SortedReflectTableModelBase) setSortFunc(sort func(col int, order SortOrder) error) {
srtmb.sort = sort
}
func (srtmb *SortedReflectTableModelBase) sorterBase() *SorterBase {
return &srtmb.SorterBase
}
func (srtmb *SortedReflectTableModelBase) Sort(col int, order SortOrder) error {
if srtmb.sort != nil {
return srtmb.sort(col, order)
}
return srtmb.SorterBase.Sort(col, order)
}
// Populator is an interface that can be implemented by Reflect*Models and slice
// types to populate themselves on demand.
//
// Widgets like TableView, ListBox and ComboBox support lazy population of a
// Reflect*Model or slice, if it implements this interface.
type Populator interface {
// Populate initializes the slot specified by index.
//
// For best performance it is probably a good idea to populate more than a
// single slot of the slice at once.
Populate(index int) error
}
// ImageProvider is the interface that a model must implement to support
// displaying an item image.
type ImageProvider interface {
// Image returns the image to display for the item at index index.
//
// Supported types are *walk.Bitmap, *walk.Icon and string. A string will be
// interpreted as a file path and the icon associated with the file will be
// used. It is not supported to use strings together with the other options
// in the same model instance.
Image(index int) interface{}
}
// CellStyler is the interface that must be implemented to provide a tabular
// widget like TableView with cell display style information.
type CellStyler interface {
// StyleCell is called for each cell to pick up cell style information.
StyleCell(style *CellStyle)
}
// CellStyle carries information about the display style of a cell in a tabular widget
// like TableView.
type CellStyle struct {
row int
col int
bounds Rectangle // in native pixels
hdc win.HDC
dpi int
canvas *Canvas
BackgroundColor Color
TextColor Color
Font *Font
// Image is the image to display in the cell.
//
// Supported types are *walk.Bitmap, *walk.Icon and string. A string will be
// interpreted as a file path and the icon associated with the file will be
// used. It is not supported to use strings together with the other options
// in the same model instance.
Image interface{}
}
func (cs *CellStyle) Row() int {
return cs.row
}
func (cs *CellStyle) Col() int {
return cs.col
}
func (cs *CellStyle) Bounds() Rectangle {
return RectangleTo96DPI(cs.bounds, cs.dpi)
}
func (cs *CellStyle) BoundsPixels() Rectangle {
return cs.bounds
}
func (cs *CellStyle) Canvas() *Canvas {
if cs.canvas != nil {
cs.canvas.dpi = cs.dpi
return cs.canvas
}
if cs.hdc != 0 {
cs.canvas, _ = newCanvasFromHDC(cs.hdc)
cs.canvas.dpi = cs.dpi
}
return cs.canvas
}
// ListItemStyler is the interface that must be implemented to provide a list
// widget like ListBox with item display style information.
type ListItemStyler interface {
// ItemHeightDependsOnWidth returns whether item height depends on width.
ItemHeightDependsOnWidth() bool
// DefaultItemHeight returns the initial height in native pixels for any item.
DefaultItemHeight() int
// ItemHeight is called for each item to retrieve the height of the item. width parameter and
// return value are specified in native pixels.
ItemHeight(index int, width int) int
// StyleItem is called for each item to pick up item style information.
StyleItem(style *ListItemStyle)
}
// ListItemStyle carries information about the display style of an item in a list widget
// like ListBox.
type ListItemStyle struct {
BackgroundColor Color
TextColor Color
LineColor Color
Font *Font
index int
hoverIndex int
rc win.RECT
bounds Rectangle // in native pixels
state uint32
hTheme win.HTHEME
hwnd win.HWND
hdc win.HDC
dpi int
canvas *Canvas
highContrastActive bool
}
func (lis *ListItemStyle) Index() int {
return lis.index
}
func (lis *ListItemStyle) Bounds() Rectangle {
return RectangleTo96DPI(lis.bounds, lis.dpi)
}
func (lis *ListItemStyle) BoundsPixels() Rectangle {
return lis.bounds
}
func (lis *ListItemStyle) Canvas() *Canvas {
if lis.canvas != nil {
lis.canvas.dpi = lis.dpi
return lis.canvas
}
if lis.hdc != 0 {
lis.canvas, _ = newCanvasFromHDC(lis.hdc)
lis.canvas.dpi = lis.dpi
}
return lis.canvas
}
func (lis *ListItemStyle) DrawBackground() error {
canvas := lis.Canvas()
if canvas == nil {
return nil
}
stateID := lis.stateID()
if lis.hTheme != 0 && stateID != win.LISS_NORMAL {
if win.FAILED(win.DrawThemeBackground(lis.hTheme, lis.hdc, win.LVP_LISTITEM, stateID, &lis.rc, nil)) {
return newError("DrawThemeBackground failed")
}
} else {
brush, err := NewSolidColorBrush(lis.BackgroundColor)
if err != nil {
return err
}
defer brush.Dispose()
if err := canvas.FillRectanglePixels(brush, lis.bounds); err != nil {
return err
}
if lis.highContrastActive && (lis.index == lis.hoverIndex || stateID != win.LISS_NORMAL) {
pen, err := NewCosmeticPen(PenSolid, lis.LineColor)
if err != nil {
return err
}
defer pen.Dispose()
if err := canvas.DrawRectanglePixels(pen, lis.bounds); err != nil {
return err
}
}
}
return nil
}
// DrawText draws text inside given bounds specified in native pixels.
func (lis *ListItemStyle) DrawText(text string, bounds Rectangle, format DrawTextFormat) error {
if lis.hTheme != 0 {
if lis.Font != nil {
hFontOld := win.SelectObject(lis.hdc, win.HGDIOBJ(lis.Font.handleForDPI(lis.dpi)))
defer win.SelectObject(lis.hdc, hFontOld)
}
rc := bounds.toRECT()
if win.FAILED(win.DrawThemeTextEx(lis.hTheme, lis.hdc, win.LVP_LISTITEM, lis.stateID(), syscall.StringToUTF16Ptr(text), int32(len(([]rune)(text))), uint32(format), &rc, nil)) {
return newError("DrawThemeTextEx failed")
}
} else {
if canvas := lis.Canvas(); canvas != nil {
if err := canvas.DrawTextPixels(text, lis.Font, lis.TextColor, bounds, format); err != nil {
return err
}
}
}
return nil
}
func (lis *ListItemStyle) stateID() int32 {
if lis.state&win.ODS_CHECKED != 0 {
if win.GetFocus() == lis.hwnd {
if lis.index == lis.hoverIndex {
return win.LISS_HOTSELECTED
} else {
return win.LISS_SELECTED
}
} else {
return win.LISS_SELECTEDNOTFOCUS
}
} else if lis.index == lis.hoverIndex {
return win.LISS_HOT
}
return win.LISS_NORMAL
}
// ItemChecker is the interface that a model must implement to support check
// boxes in a widget like TableView.
type ItemChecker interface {
// Checked returns if the specified item is checked.
Checked(index int) bool
// SetChecked sets if the specified item is checked.
SetChecked(index int, checked bool) error
}
// SortOrder specifies the order by which items are sorted.
type SortOrder int
const (
// SortAscending specifies ascending sort order.
SortAscending SortOrder = iota
// SortDescending specifies descending sort order.
SortDescending
)
// Sorter is the interface that a model must implement to support sorting with a
// widget like TableView.
type Sorter interface {
// ColumnSortable returns whether column col is sortable.
ColumnSortable(col int) bool
// Sort sorts column col in order order.
//
// If col is -1 then no column is to be sorted. Sort must publish the event
// returned from SortChanged() after sorting.
Sort(col int, order SortOrder) error
// SortChanged returns an event that is published after sorting.
SortChanged() *Event
// SortedColumn returns the index of the currently sorted column, or -1 if
// no column is currently sorted.
SortedColumn() int
// SortOrder returns the current sort order.
SortOrder() SortOrder
}
// SorterBase implements the Sorter interface.
//
// You still need to provide your own implementation of at least the Sort method
// to actually sort and reset the model. Your Sort method should call the
// SorterBase implementation so the SortChanged event, that e.g. a TableView
// widget depends on, is published.
type SorterBase struct {
changedPublisher EventPublisher
col int
order SortOrder
}
func (sb *SorterBase) ColumnSortable(col int) bool {
return true
}
func (sb *SorterBase) Sort(col int, order SortOrder) error {
sb.col, sb.order = col, order
sb.changedPublisher.Publish()
return nil
}
func (sb *SorterBase) SortChanged() *Event {
return sb.changedPublisher.Event()
}
func (sb *SorterBase) SortedColumn() int {
return sb.col
}
func (sb *SorterBase) SortOrder() SortOrder {
return sb.order
}
// Imager provides access to an image of objects like tree items.
type Imager interface {
// Image returns the image to display for an item.
//
// Supported types are *walk.Bitmap, *walk.Icon and string. A string will be
// interpreted as a file path and the icon associated with the file will be
// used. It is not supported to use strings together with the other options
// in the same model instance.
Image() interface{}
}
// TreeItem represents an item in a TreeView widget.
type TreeItem interface {
// Text returns the text of the item.
Text() string
// Parent returns the parent of the item.
Parent() TreeItem
// ChildCount returns the number of children of the item.
ChildCount() int
// ChildAt returns the child at the specified index.
ChildAt(index int) TreeItem
}
// HasChilder enables widgets like TreeView to determine if an item has any
// child, without enforcing to fully count all children.
type HasChilder interface {
HasChild() bool
}
// TreeModel provides widgets like TreeView with item data.
type TreeModel interface {
// LazyPopulation returns if the model prefers on-demand population.
//
// This is useful for models that potentially contain huge amounts of items,
// e.g. a model that represents a file system.
LazyPopulation() bool
// RootCount returns the number of root items.
RootCount() int
// RootAt returns the root item at the specified index.
RootAt(index int) TreeItem
// ItemsReset returns the event that the model should publish when the
// descendants of the specified item, or all items if no item is specified,
// are reset.
ItemsReset() *TreeItemEvent
// ItemChanged returns the event that the model should publish when an item
// was changed.
ItemChanged() *TreeItemEvent
// ItemInserted returns the event that the model should publish when an item
// was inserted into the model.
ItemInserted() *TreeItemEvent
// ItemRemoved returns the event that the model should publish when an item
// was removed from the model.
ItemRemoved() *TreeItemEvent
}
// TreeModelBase partially implements the TreeModel interface.
//
// You still need to provide your own implementation of at least the
// RootCount and RootAt methods. If your model needs lazy population,
// you will also have to implement LazyPopulation.
type TreeModelBase struct {
itemsResetPublisher TreeItemEventPublisher
itemChangedPublisher TreeItemEventPublisher
itemInsertedPublisher TreeItemEventPublisher
itemRemovedPublisher TreeItemEventPublisher
}
func (tmb *TreeModelBase) LazyPopulation() bool {
return false
}
func (tmb *TreeModelBase) ItemsReset() *TreeItemEvent {
return tmb.itemsResetPublisher.Event()
}
func (tmb *TreeModelBase) ItemChanged() *TreeItemEvent {
return tmb.itemChangedPublisher.Event()
}
func (tmb *TreeModelBase) ItemInserted() *TreeItemEvent {
return tmb.itemInsertedPublisher.Event()
}
func (tmb *TreeModelBase) ItemRemoved() *TreeItemEvent {
return tmb.itemRemovedPublisher.Event()
}
func (tmb *TreeModelBase) PublishItemsReset(parent TreeItem) {
tmb.itemsResetPublisher.Publish(parent)
}
func (tmb *TreeModelBase) PublishItemChanged(item TreeItem) {
tmb.itemChangedPublisher.Publish(item)
}
func (tmb *TreeModelBase) PublishItemInserted(item TreeItem) {
tmb.itemInsertedPublisher.Publish(item)
}
func (tmb *TreeModelBase) PublishItemRemoved(item TreeItem) {
tmb.itemRemovedPublisher.Publish(item)
}