base

package
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2021 License: BSD-3-Clause Imports: 2 Imported by: 17

Documentation

Overview

Package base provides interfaces for the description, creation, and updating of GUI widgets. Any users interested in creating new widgets will need to implement the interfaces Widget and Element described herein.

GUI state is managed using three groups of types. There are 'widgets', which are data-only representations of a desired GUI. These widgets can be mounted to create 'elements', which manage actual, visible GUI resources. The elements manage child elements, some number of platform-specific resources, called 'controls', or both.

Additionally, this package contains geometric types. These types support the automatic layout of widgets in a platform independent manner. The layout algorithm is roughly based on Flutter. For example, see the type Constraints, which is roughly similar to flutter's BoxConstraints.

Index

Examples

Constants

View Source
const (
	// PLATFORM specifies the GUI toolkit being used.
	PLATFORM = "gtk"
)

Variables

View Source
var (
	// DPI contains the current DPI (dots per inch) of the monitor.
	// User code should not need to set this directly, as drivers will update
	// this variable as necessary.
	DPI image.Point
)

Functions

func CloseElements

func CloseElements(children []Element)

CloseElements closes all of the elements contained in a slice. This is a utility function to help containers close all of their children when required.

Types

type Constraints

type Constraints struct {
	Min, Max Size
}

Constraints represents box constraints on width and height for the layout of rectangular widgets. For each dimension, the constraints specify the minimum and maximum allowed size for a widget.

The constraints on a dimension are called 'tight' if the minimum and maximum values are equal, which essential requires the widget to take a fixed size. On the other hand, if the minimum allowed value is zero, then the constraints on that dimension is 'loose'.

A sentinel value can be used to indicate that the maximum size for a dimension is infinite. The constraints on that dimension are called 'unbounded'.

func Loose

func Loose(size Size) Constraints

Loose creates box constraints that forbid sizes larger than the given size. The constraints for both width and height will be loose and bounded.

func LooseHeight added in v0.9.0

func LooseHeight(height Length) Constraints

LooseHeight creates box constraints that forbid heights larger than the given length. The constraints for height will be loose and bounded, while the width will be unconstrained.

func LooseWidth added in v0.9.0

func LooseWidth(width Length) Constraints

LooseWidth creates box constraints that forbid widths larger than the given length. The constraints for width will be loose and bounded, while the height will be unconstrained.

func Tight

func Tight(size Size) Constraints

Tight creates a box constraints that is respected only by the given size.

func TightHeight

func TightHeight(height Length) Constraints

TightHeight creates a box constraints that is respected only by sizes with the given height. The width is unconstrained (i.e. loose and unbounded).

func TightWidth

func TightWidth(width Length) Constraints

TightWidth creates a box constraints that is respected only by sizes with the given width. The height is unconstrained (i.e. loose and unbounded).

func Unconstrained added in v0.9.0

func Unconstrained() Constraints

Unconstrained creates box constraints that allow any size. The constraints for both widthand height will be loose and unbounded.

func (Constraints) Constrain

func (bc Constraints) Constrain(size Size) Size

Constrain returns the size that satisfies the constraints while staying as close as possible to the passed size.

func (Constraints) ConstrainAndAttemptToPreserveAspectRatio

func (bc Constraints) ConstrainAndAttemptToPreserveAspectRatio(size Size) Size

ConstrainAndAttemptToPreserveAspectRatio returns the size that satisfies the constraints while staying close to the passed size and maintaining the aspect ratio of the passed size.

func (Constraints) ConstrainHeight

func (bc Constraints) ConstrainHeight(height Length) Length

ConstrainHeight returns the length that satisfies the constraints for height while staying as close as possible to the passed height.

func (Constraints) ConstrainWidth

func (bc Constraints) ConstrainWidth(width Length) Length

ConstrainWidth returns the length that satisfies the constraints for width while staying as close as possible to the passed height.

func (Constraints) HasBoundedHeight

func (bc Constraints) HasBoundedHeight() bool

HasBoundedHeight is true if the maximum height is bounded.

func (Constraints) HasBoundedWidth

func (bc Constraints) HasBoundedWidth() bool

HasBoundedWidth is true if the maximum width is bounded.

func (Constraints) HasTightHeight

func (bc Constraints) HasTightHeight() bool

HasTightHeight is true if the height is tight (only one value of height satisfies the constraint).

func (Constraints) HasTightWidth

func (bc Constraints) HasTightWidth() bool

HasTightWidth is true if the width is tight (only one value of width satisfies the constraint).

func (Constraints) Inset

func (bc Constraints) Inset(width Length, height Length) Constraints

Inset returns a new set of box constraints such that a size that satisfies those new constraints can be increased by width and height and will satisfy the original constrains.

func (Constraints) IsBounded

func (bc Constraints) IsBounded() bool

IsBounded is true if both the width and height are bounded.

func (Constraints) IsNormalized

func (bc Constraints) IsNormalized() bool

IsNormalized is true if both the width and height constraints are normalized. A set of constraints are normalized if 0 <= Min <= Max.

func (Constraints) IsSatisfiedBy

func (bc Constraints) IsSatisfiedBy(size Size) bool

IsSatisfiedBy returns true if the passed size satisfies the both the width and height constraints. Additionally, both width and height must be finite (i.e. not equal to the sentinal value Inf).

func (Constraints) IsTight

func (bc Constraints) IsTight() bool

IsTight returns true if both the width and height are tightly constrained.

func (Constraints) IsZero

func (bc Constraints) IsZero() bool

IsZero returns true if the bc is the zero value.

func (Constraints) Loosen

func (bc Constraints) Loosen() Constraints

Loosen creates a new box constraint with the minimum width and height requirements removed.

func (Constraints) LoosenHeight

func (bc Constraints) LoosenHeight() Constraints

LoosenHeight creates a new box constraint with the minimum height requirement removed.

func (Constraints) LoosenWidth

func (bc Constraints) LoosenWidth() Constraints

LoosenWidth creates a new box constraint with the minimum width requirement removed.

func (Constraints) String added in v0.9.0

func (bc Constraints) String() string

func (Constraints) Tighten

func (bc Constraints) Tighten(size Size) Constraints

Tighten creates a new box constraint with tight width and height requirements matching as closely as possible the passed size. The new constrains will be tight, but will only match the requested size if the size satisfies the original constraints.

func (Constraints) TightenHeight

func (bc Constraints) TightenHeight(height Length) Constraints

TightenHeight creates a new box constraint with a tight height requirements matching as closely as possible the length. The new height constraints will be tight, but will only match the requested height if the height satisfies the original constraints.

func (Constraints) TightenWidth

func (bc Constraints) TightenWidth(width Length) Constraints

TightenWidth creates a new box constraint with a tight width requirements matching as closely as possible the length. The new width constraints will be tight, but will only match the requested width if the width satisfies the original constraints.

type Control

type Control struct {
	Handle uintptr
}

Control is an opaque type used as a platform-specific handle to a control created using the platform GUI. As an example, this will refer to a HWND when targeting Windows, but a *GtkContainer when targeting GTK.

Unless developing new widgets, users should not need to use this type.

Any methods on this type will be platform specific.

type Element

type Element interface {
	// NativeElement provides platform-dependent methods.  These should
	// not be used by client libraries, but exist for the internal implementation
	// of platform dependent code.
	NativeElement

	// Close removes the element from the GUI, and frees any associated resources.
	Close()
	// Kind returns the concrete type for the Element.
	// Users should not need to use this method directly.
	Kind() *Kind
	// Layout determines the best size for an element that satisfies the
	// constraints.
	Layout(Constraints) Size
	// MinIntrinsicHeight returns the minimum height that this element requires
	// to be correctly displayed.
	MinIntrinsicHeight(width Length) Length
	// MinIntrinsicWidth returns the minimum width that this element requires
	// to be correctly displayed.
	MinIntrinsicWidth(height Length) Length
	// SetBounds updates the position of the widget.
	SetBounds(bounds Rectangle)
	// UpdateProps will update the properties of the widget.  The Kind for
	// the parameter data must match the Kind for the interface.
	UpdateProps(data Widget) error
}

Element is an interface that wraps any type representing a control, or group of controls, created using the platform GUI. An element represents an instantiation of a Widget into visible parts of the GUI.

func DiffChild

func DiffChild(parent Control, lhs Element, rhs Widget) (Element, error)

DiffChild adds and removes controls in a GUI to reconcile differences between the desired and current GUI state. Depending on the kind for both lhs and rhs, the current element may either be updated or replaced.

The element lhs should be considered as 'sunk' by this function, and will be closed if necessary. On the other hand, the caller will be responsible for the returned element. Note that the returned element may be non-nil even in the presence of an error.

If the rhs is nil, DiffChild will still return a non-nil element. See the function Method for more details.

func DiffChildren

func DiffChildren(parent Control, lhs []Element, rhs []Widget) ([]Element, error)

DiffChildren will update the list of elements to match the desired state. As necessary, widgets will be mounted to create new elements, and existing elements will be updated or closed to reconcile differences between the desired and current GUI state.

The elements contained in lhs should be considered as 'sunk' by this function, and will be closed if necessary. On the other hand, the caller will be responsible for all of the returned elements. Note that the returned slice of elements may be non-nil even in the presence of an error. Use CloseElements as necessary to avoid leaking controls.

DiffChildren will try to reuse the underlying array from lhs for the returned slice.

func Mount

func Mount(parent Control, widget Widget) (Element, error)

Mount will try to mount a widget. In the case where the widget is non-nil, this function is a simple wrapper around calling the method Mount directly. If widget is nil, this function will instead return a non-nil element to act as a placeholder.

The placeholder element has an intrinsic size of zero and adds no visible elements in the GUI. Unlike other elements, there is no need to call Close, as that method is a no-op.

Example
// This won't work in real code, as the zero value for a control is not
// generally useable.
parent := Control{}

widget := &mock{}
elem, err := Mount(parent, widget)
if err != nil {
	panic("Unexpected error!")
}
defer elem.Close()

if widget.Kind() != elem.Kind() {
	panic("Internal error, kinds do not match")
}

fmt.Println("OK")
Output:

OK
Example (Nil)
// This won't work in real code, as the zero value for a control is not
// generally useable.
parent := Control{}

// It is okay to mount a nil widget.
elem, err := Mount(parent, nil)
if err != nil {
	panic("Unexpected error!")
}
defer elem.Close()
fmt.Println("The value of elem is nil...", elem == nil)
fmt.Println("The kind of elem is...", elem.Kind())
fmt.Println("The minimum intrinsic height is...", elem.MinIntrinsicHeight(Inf))
fmt.Println("The minimum intrinsic width is...", elem.MinIntrinsicWidth(Inf))
Output:

The value of elem is nil... false
The kind of elem is... bitbucket.org/rj/goey/base.nil
The minimum intrinsic height is... 0:00
The minimum intrinsic width is... 0:00

func MountNil added in v0.9.0

func MountNil() Element

MountNil is a wrapper around Mount(parent,nil).

type Kind

type Kind struct {
	// contains filtered or unexported fields
}

Kind identifies the different kinds of widgets. Most widgets have two concrete types associated with their behavior. First, there is a type with data to describe the widget when unmounted, which should implement the interface Widget. Second, there is a type with a handle to the windowing system when mounted, which should implement the interface Element. Automatic reconciliation of two widget trees relies on Kind to match the unmounted and mounted widgets.

Note that comparison of kinds is done by address, and not done using the value of any fields. Any internal state is simply to help with debugging.

func NewKind

func NewKind(name string) Kind

NewKind creates a new kind. The name should identify the type used for the widget, but is currently unused.

func (Kind) String

func (k Kind) String() string

String returns the string with the name of the widget and element kind.

Example
package main

import (
	"fmt"

	"bitbucket.org/rj/goey/base"
)

func main() {
	kind := base.NewKind("bitbucket.org/rj/goey/base.Example")

	fmt.Println("Kind is", kind.String())

}
Output:

Kind is bitbucket.org/rj/goey/base.Example

type Length

type Length fixed.Int26_6

Length is a distance measured in device-independent pixels. There are nominally 96 DIPs per inch. This definition corresponds with the definition of a pixel for both CSS and on Windows.

Example
package main

import (
	"fmt"

	"bitbucket.org/rj/goey/base"
)

func main() {
	// Since there are 96 device-independent pixels per inch, and 6 picas
	// per inch, the following two lengths should be equal.
	length1 := 96 * base.DIP
	length2 := 6 * base.PC

	if length1 == length2 {
		fmt.Printf("All is OK with the world.")
	} else {
		fmt.Printf("This should not happen, unless there is a rounding error.")
	}

}
Output:

All is OK with the world.
const (
	DIP  Length = (1 << 6)         // Device-independent pixel (1/96 inch)
	PT   Length = ((96 << 6) / 72) // Point (1/72 inch)
	PC   Length = ((96 << 6) / 6)  // Pica (1/6 inch or 12 points)
	Inch Length = (96 << 6)        // Inch from the British imperial system of measurements
)

Common lengths used when describing GUIs. Note that the DIP (device-independent pixel) is the natural unit for this package. Because of limited precision, the PT listed here is somewhat smaller than its correct value.

const (
	// Inf is a sentinel value indicating an unbounded (or infinite) length.
	Inf Length = 0x7fffffff
)

func FromPixelsX

func FromPixelsX(pixels int) Length

FromPixelsX converts a distance measurement in physical pixels to DIPs, based on the current DPI settings for horizontal scaling.

func FromPixelsY

func FromPixelsY(pixels int) Length

FromPixelsY converts a distance measurement in physical pixels to DIPs, based on the current DPI settings for vertical scaling.

func (Length) Clamp

func (v Length) Clamp(min, max Length) Length

Clamp ensures that the length is between the minimum and maximum values specified. Normally, min should be less than max. If that is not the case, then the returned will preferentially respect min.

func (Length) DIP

func (v Length) DIP() float64

DIP returns a float64 with the length measured in device independent pixels.

func (Length) Inch

func (v Length) Inch() float64

Inch returns a float64 with the length measured in inches.

func (Length) PC

func (v Length) PC() float64

PC returns a float64 with the length measured in picas.

func (Length) PT

func (v Length) PT() float64

PT returns a float64 with the length measured in points.

func (Length) PixelsX

func (v Length) PixelsX() int

PixelsX converts the distance measurement in DIPs to physical pixels, based on the current DPI settings for horizontal scaling.

func (Length) PixelsY

func (v Length) PixelsY() int

PixelsY converts the distance measurement in DIPs to physical pixels, based on the current DPI settings for vertical scaling.

func (Length) Scale

func (v Length) Scale(num, den int) Length

Scale scales the distance by the ratio of num:den.

Example
package main

import (
	"fmt"

	"bitbucket.org/rj/goey/base"
)

func main() {
	// There are 96 DIP in an inch, and 6 pica in a inch, so the following
	// should work.

	if length := (1 * base.DIP).Scale(96, 6); length == (1 * base.PC) {
		fmt.Printf("The ratio of pica to DIP is 96 to 6.")
	}

}
Output:

The ratio of pica to DIP is 96 to 6.

func (Length) String

func (v Length) String() string

String returns a human readable distance.

Example
package main

import (
	"fmt"

	"bitbucket.org/rj/goey/base"
)

func main() {
	fmt.Printf("Converting:  1pt is equal to %sdip\n", 1*base.PT)
	fmt.Printf("Converting:  1pt is equal to %1.2fdip\n", (1 * base.PT).DIP())
	fmt.Printf("Converting:  1pc is equal to %1.1fdip\n", (1 * base.PC).DIP())

}
Output:

Converting:  1pt is equal to 1:21dip
Converting:  1pt is equal to 1.33dip
Converting:  1pc is equal to 16.0dip

type NativeElement

type NativeElement interface{}

NativeElement contains platform-specific methods that all widgets must support on GTK.

type Point

type Point struct {
	X, Y Length
}

A Point is an X, Y coordinate pair. The axes increase right and down. This type is a close analogy to image.Pixel, except that the coordinate pair is represented by Length rather than int.

func (Point) Add

func (p Point) Add(q Point) Point

Add returns the vector p+q.

func (Point) Pixels

func (p Point) Pixels() image.Point

Pixels returns the vector with the X and Y coordinates measured in pixels.

func (Point) String

func (p Point) String() string

String returns a string representation of p like "(3,4)".

func (Point) Sub

func (p Point) Sub(q Point) Point

Sub returns the vector p-q.

type Rectangle

type Rectangle struct {
	Min, Max Point
}

A Rectangle contains the points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y. It is well-formed if Min.X <= Max.X and likewise for Y. Points are always well-formed. A rectangle's methods always return well-formed outputs for well-formed inputs.

This type is a close analogy to image.Rectangle, except that the coordinates are represented by Length rather than int.

Example
package main

import (
	"fmt"

	"bitbucket.org/rj/goey/base"
)

func main() {
	r := base.Rectangle{
		Min: base.Point{10 * base.DIP, 20 * base.DIP},
		Max: base.Point{90 * base.DIP, 80 * base.DIP},
	}

	fmt.Printf("Rectangle %s has dimensions %.0fdip by %.0fdip.",
		r, r.Dx().DIP(), r.Dy().DIP(),
	)

}
Output:

Rectangle (10:00,20:00)-(90:00,80:00) has dimensions 80dip by 60dip.

func Rect

func Rect(x0, y0, x1, y1 Length) Rectangle

Rect is shorthand for Rectangle{Point(x0, y0), Point(x1, y1)}. The returned rectangle has minimum and maximum coordinates swapped if necessary so that it is well-formed.

func (Rectangle) Add

func (r Rectangle) Add(p Point) Rectangle

Add returns the rectangle r translated by p.

Example
package main

import (
	"fmt"

	"bitbucket.org/rj/goey/base"
)

func main() {
	r := base.Rectangle{
		Min: base.Point{10 * base.DIP, 20 * base.DIP},
		Max: base.Point{90 * base.DIP, 80 * base.DIP},
	}
	v := base.Point{5 * base.DIP, 5 * base.DIP}

	fmt.Printf("Rectangle %s, moved by %s,\n", r, v)
	fmt.Printf("---- %s", r.Add(v))

}
Output:

Rectangle (10:00,20:00)-(90:00,80:00), moved by (5:00,5:00),
---- (15:00,25:00)-(95:00,85:00)

func (Rectangle) Dx

func (r Rectangle) Dx() Length

Dx returns r's width.

func (Rectangle) Dy

func (r Rectangle) Dy() Length

Dy returns r's height.

func (Rectangle) Pixels

func (r Rectangle) Pixels() image.Rectangle

Pixels returns the rectangle with the X and Y coordinates measured in pixels.

Example
package main

import (
	"fmt"
	"image"

	"bitbucket.org/rj/goey/base"
)

func main() {
	// The following line is for the example only, and should not appear in
	// user code, as the platform-specific code should update the DPI based
	// on the system.  However, for the purpose of this example, set a known
	// DPI.
	base.DPI = image.Point{2 * 96, 2 * 96}

	// Construct an example rectangle.
	r := base.Rectangle{
		Min: base.Point{10 * base.DIP, 20 * base.DIP},
		Max: base.Point{90 * base.DIP, 80 * base.DIP},
	}
	rpx := r.Pixels()

	fmt.Printf("Rectangle %s when translated to pixels is %s.", r, rpx)

}
Output:

Rectangle (10:00,20:00)-(90:00,80:00) when translated to pixels is (20,40)-(180,160).

func (Rectangle) Size

func (r Rectangle) Size() Point

Size returns r's width and height.

func (Rectangle) String

func (r Rectangle) String() string

String returns a string representation of r like "(3,4)-(6,5)".

type Size

type Size struct {
	Width, Height Length
}

Size represents the size of a rectangular element.

func FromPixels

func FromPixels(x, y int) Size

FromPixels converts the pixels into lengths based on the current DPI, and return the size.

Example
package main

import (
	"fmt"
	"image"

	"bitbucket.org/rj/goey/base"
)

func main() {
	// Most code should not need to worry about setting the DPI.  Windows will
	// ensure that the DPI is set.
	base.DPI = image.Point{96, 96}

	size := base.FromPixels(48, 96+96)
	fmt.Printf("The size is %s.\n", size)

}
Output:

The size is (48:00x192:00).

func (Size) IsZero

func (s Size) IsZero() bool

IsZero returns true if the size is the zero value.

func (Size) String

func (s Size) String() string

String returns a string representation of the size.

type Widget

type Widget interface {
	// Kind returns the concrete type's Kind.  The returned value should
	// be constant, and the same for all instances of a concrete type.
	// Users should not need to use this method directly.
	Kind() *Kind
	// Mount creates a widget or control in the GUI.  The newly created widget
	// will be a child of the widget specified by parent.  If non-nil, the
	// returned Element must have a matching kind.  Must return either an
	// Element or an error.
	Mount(parent Control) (Element, error)
}

Widget is an interface that wraps any type describing part of a GUI. A widget can be 'mounted' to create controls using the platform GUI.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL