From 3ce04a4d3dc8d174b520d85804e5c8dce8c5d08f Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Tue, 10 Feb 2026 09:30:42 -0500 Subject: add layout resize functions --- layout/doc.go | 15 +++++++++++++ layout/draw.go | 27 ++++++++++++++++++++++++ layout/layout.go | 28 ------------------------- layout/resize.go | 35 +++++++++++++++++++++++++++++++ layout/resize_test.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ layout/subimage.go | 17 --------------- test/region.go | 10 ++++----- 7 files changed, 140 insertions(+), 50 deletions(-) create mode 100644 layout/doc.go create mode 100644 layout/draw.go delete mode 100644 layout/layout.go create mode 100644 layout/resize.go create mode 100644 layout/resize_test.go delete mode 100644 layout/subimage.go diff --git a/layout/doc.go b/layout/doc.go new file mode 100644 index 0000000..bae4673 --- /dev/null +++ b/layout/doc.go @@ -0,0 +1,15 @@ +/* +Package layout provides means of partitioning screen space. + +A layout exists in a parent Env, and has one or more child Envs. It +acts as a multiplexer for the children. The parent Env may be a window, +another layout, or whatever... Several layers of layouts can be composed. + +A layout allocates screen area to its children by intercepting Resize +events from the parent Env. Upon reception by the layout, a Resize event +is transformed for each child, and forwarded to them. + +Draw calls from the children are intercepted and translated onto their +respective areas before being forwarded to the parent Env. +*/ +package layout diff --git a/layout/draw.go b/layout/draw.go new file mode 100644 index 0000000..760a149 --- /dev/null +++ b/layout/draw.go @@ -0,0 +1,27 @@ +package layout + +import ( + "image" + "image/color" + "image/draw" +) + +// subimager is implemented by most Image types. +type subimager interface { + SubImage(image.Rectangle) image.Image +} + +// Subimage returns an image representing the portion of the image m visible through r. +// The returned value shares pixels with the original. +// Panics if the concrete image type does not have a SubImage() method. +func subimage(m draw.Image, r image.Rectangle) draw.Image { + return m.(subimager).SubImage(r).(draw.Image) +} + +// drawBackground returns a draw call that fills the entire image with a color. +func drawBackground(c color.Color) func(draw.Image) image.Rectangle { + return func(img draw.Image) image.Rectangle { + draw.Draw(img, img.Bounds(), &image.Uniform{c}, image.ZP, draw.Src) + return img.Bounds() + } +} diff --git a/layout/layout.go b/layout/layout.go deleted file mode 100644 index b04aa35..0000000 --- a/layout/layout.go +++ /dev/null @@ -1,28 +0,0 @@ -/* -Package layout provides means of partitioning screen space. - -A layout exists in a parent Env, and has one or more child Envs. It -acts as a multiplexer for the children. The parent Env may be a window, -another layout, or whatever... Several layers of layouts can be composed. - -A layout allocates screen area to its children by intercepting Resize -events from the parent Env. Upon reception by the layout, a Resize event -is transformed for each child, and forwarded to them. - -Draw calls from the children are intercepted and translated onto their -respective areas before being forwarded to the parent Env. -*/ -package layout - -import ( - "image" - "image/color" - "image/draw" -) - -func drawBackground(c color.Color) func(draw.Image) image.Rectangle { - return func(img draw.Image) image.Rectangle { - draw.Draw(img, img.Bounds(), &image.Uniform{c}, image.ZP, draw.Src) - return img.Bounds() - } -} diff --git a/layout/resize.go b/layout/resize.go new file mode 100644 index 0000000..1d3d6f1 --- /dev/null +++ b/layout/resize.go @@ -0,0 +1,35 @@ +package layout + +import "image" + +// ResizeAll resizes a layout to consume all of its parent's area. +func ResizeAll(r image.Rectangle) image.Rectangle { return r } + +// ResizeQuad1 resizes a layout to consume the top-right quadrant of its parent. +func ResizeQuad1(r image.Rectangle) image.Rectangle { + mid := midpoint(r) + return image.Rect(mid.X, r.Min.Y, r.Max.X, mid.Y) +} + +// ResizeQuad2 resizes a layout to consume the top-left quadrant of its parent. +func ResizeQuad2(r image.Rectangle) image.Rectangle { + mid := midpoint(r) + return image.Rectangle{r.Min, mid} +} + +// ResizeQuad3 resizes a layout to consume the bottom-left quadrant of its parent. +func ResizeQuad3(r image.Rectangle) image.Rectangle { + mid := midpoint(r) + return image.Rect(r.Min.X, mid.Y, mid.X, r.Max.Y) +} + +// ResizeQuad4 resizes a layout to consume the bottom-right quadrant of its parent. +func ResizeQuad4(r image.Rectangle) image.Rectangle { + mid := midpoint(r) + return image.Rectangle{mid, r.Max} +} + +// midpoint returns the point in the middle of a rectangle. +func midpoint(r image.Rectangle) image.Point { + return r.Min.Add(r.Max).Div(2) +} diff --git a/layout/resize_test.go b/layout/resize_test.go new file mode 100644 index 0000000..5be7a41 --- /dev/null +++ b/layout/resize_test.go @@ -0,0 +1,58 @@ +package layout_test + +import ( + "image" + "testing" + + "github.com/faiface/gui/layout" +) + +func TestResizeAll(t *testing.T) { + t.Parallel() + parent := image.Rect(111, 222, 333, 444) + child := layout.ResizeAll(parent) + want := parent + if child != want { + t.Errorf("got %v; want %v", child, want) + } +} + +func TestResizeQuad1(t *testing.T) { + t.Parallel() + parent := image.Rect(111, 222, 333, 444) + child := layout.ResizeQuad1(parent) + want := image.Rect(222, 222, 333, 333) + if child != want { + t.Errorf("got %v; want %v", child, want) + } +} + +func TestResizeQuad2(t *testing.T) { + t.Parallel() + parent := image.Rect(111, 222, 333, 444) + child := layout.ResizeQuad2(parent) + want := image.Rect(111, 222, 222, 333) + if child != want { + t.Errorf("got %v; want %v", child, want) + } +} + +func TestResizeQuad3(t *testing.T) { + t.Parallel() + parent := image.Rect(111, 222, 333, 444) + child := layout.ResizeQuad3(parent) + want := image.Rect(111, 333, 222, 444) + if child != want { + t.Errorf("got %v; want %v", child, want) + } +} + +func TestResizeQuad4(t *testing.T) { + t.Parallel() + parent := image.Rect(111, 222, 333, 444) + child := layout.ResizeQuad4(parent) + want := image.Rect(222, 333, 333, 444) + if child != want { + t.Errorf("got %v; want %v", child, want) + } +} diff --git a/layout/subimage.go b/layout/subimage.go deleted file mode 100644 index ecbe931..0000000 --- a/layout/subimage.go +++ /dev/null @@ -1,17 +0,0 @@ -package layout - -import ( - "image" - "image/draw" -) - -type subimager interface { - SubImage(image.Rectangle) image.Image -} - -// Subimage returns an image representing the portion of the image m visible through r. -// The returned value shares pixels with the original. -// Panics if the concrete image type does not have a SubImage() method. -func subimage(m draw.Image, r image.Rectangle) draw.Image { - return m.(subimager).SubImage(r).(draw.Image) -} diff --git a/test/region.go b/test/region.go index 0b99a3a..682e23f 100644 --- a/test/region.go +++ b/test/region.go @@ -27,11 +27,11 @@ func run() { mux, env := gui.NewMux(w) - // Create region in bottom-right quadrant of window - resize := func(r image.Rectangle) image.Rectangle { - return image.Rect(r.Min.X+r.Dx()/2, r.Min.Y+r.Dy()/2, r.Max.X, r.Max.Y) - } - region := layout.NewRegion(mux.MakeEnv(), resize, layout.Background(bg)) + // Background + back := layout.NewRegion(mux.MakeEnv(), layout.ResizeAll, layout.Background(bg)) + + // Create region on top of background, in bottom-right quadrant + region := layout.NewRegion(back, layout.ResizeQuad4) go blinker(region) for event := range env.Events() { -- cgit v1.2.3