mirror of
https://github.com/diamondburned/cchat-gtk.git
synced 2024-12-23 20:56:42 +00:00
131 lines
2.8 KiB
Go
131 lines
2.8 KiB
Go
package roundimage
|
|
|
|
import (
|
|
"math"
|
|
|
|
"github.com/diamondburned/cchat-gtk/internal/ui/primitives"
|
|
"github.com/gotk3/gotk3/cairo"
|
|
"github.com/gotk3/gotk3/gtk"
|
|
)
|
|
|
|
const (
|
|
pi = math.Pi
|
|
circle = 2 * math.Pi
|
|
)
|
|
|
|
// Button implements a rounded button with a rounded image. This widget only
|
|
// supports a full circle for rounding.
|
|
type Button struct {
|
|
*gtk.Button
|
|
Image *Image
|
|
}
|
|
|
|
var roundButtonCSS = primitives.PrepareClassCSS("round-button", `
|
|
.round-button {
|
|
padding: 0;
|
|
border-radius: 50%;
|
|
}
|
|
`)
|
|
|
|
func NewButton() (*Button, error) {
|
|
image, _ := NewImage(0)
|
|
image.Show()
|
|
|
|
b, _ := gtk.ButtonNew()
|
|
b.SetImage(image)
|
|
b.SetRelief(gtk.RELIEF_NONE)
|
|
roundButtonCSS(b)
|
|
|
|
return &Button{Button: b, Image: image}, nil
|
|
}
|
|
|
|
type Image struct {
|
|
*gtk.Image
|
|
Radius float64
|
|
}
|
|
|
|
// NewImage creates a new round image. If radius is 0, then it will be half the
|
|
// dimensions. If the radius is less than 0, then nothing is rounded.
|
|
func NewImage(radius float64) (*Image, error) {
|
|
i, err := gtk.ImageNew()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
image := &Image{Image: i, Radius: radius}
|
|
|
|
// Connect to the draw callback and clip the context.
|
|
i.Connect("draw", image.drawer)
|
|
|
|
return image, nil
|
|
}
|
|
|
|
func (i *Image) SetRadius(r float64) {
|
|
i.Radius = r
|
|
}
|
|
|
|
func (i *Image) drawer(widget gtk.IWidget, cc *cairo.Context) bool {
|
|
var w = float64(i.GetAllocatedWidth())
|
|
var h = float64(i.GetAllocatedHeight())
|
|
|
|
var min = w
|
|
// Use the smallest side for radius calculation.
|
|
if h < w {
|
|
min = h
|
|
}
|
|
|
|
// Copy the variables in case we need to change them.
|
|
var r = i.Radius
|
|
|
|
switch {
|
|
// If radius is less than 0, then don't round.
|
|
case r < 0:
|
|
return false
|
|
|
|
// If radius is 0, then we have to calculate our own radius.:This only
|
|
// works if the image is a square.
|
|
case r == 0:
|
|
// Calculate the radius by dividing a side by 2.
|
|
r = (min / 2)
|
|
|
|
// Draw an arc from 0deg to 360deg.
|
|
cc.Arc(w/2, h/2, r, 0, circle)
|
|
|
|
// We have to do this so the arc paint doesn't leave back a black
|
|
// background instead of the usual alpha.
|
|
cc.SetSourceRGBA(255, 255, 255, 0)
|
|
|
|
// Clip the image with the arc we drew.
|
|
cc.Clip()
|
|
|
|
// If radius is more than 0, then we have to calculate the radius from
|
|
// the edges.
|
|
case r > 0:
|
|
// StackOverflow is godly.
|
|
// https://stackoverflow.com/a/6959843.
|
|
|
|
// Radius should be largest a single side divided by 2.
|
|
if max := min / 2; r > max {
|
|
r = max
|
|
}
|
|
|
|
// Draw 4 arcs at 4 corners.
|
|
cc.Arc(0+r, 0+r, r, 2*(pi/2), 3*(pi/2)) // top left
|
|
cc.Arc(w-r, 0+r, r, 3*(pi/2), 4*(pi/2)) // top right
|
|
cc.Arc(w-r, h-r, r, 0*(pi/2), 1*(pi/2)) // bottom right
|
|
cc.Arc(0+r, h-r, r, 1*(pi/2), 2*(pi/2)) // bottom left
|
|
|
|
// Close the created path.
|
|
cc.ClosePath()
|
|
cc.SetSourceRGBA(255, 255, 255, 0)
|
|
|
|
// Clip the image with the arc we drew.
|
|
cc.Clip()
|
|
}
|
|
|
|
// Paint the changes.
|
|
cc.Paint()
|
|
|
|
return false
|
|
}
|