awesome-wm-nice/shapes.lua

226 lines
7.5 KiB
Lua

-- => Shapes
-- Provides utility functions for handling cairo shapes and geometry
-- ============================================================
--
local lgi = require("lgi")
local colors = require("nice.colors")
local hex2rgb = colors.hex2rgb
local darken = colors.darken
local cairo = lgi.cairo
local math = math
local rad = math.rad
local floor = math.floor
local min = math.min
-- Returns a shape function for a rounded rectangle with independently configurable corner radii
local function rounded_rect(args)
local r1 = args.tl or 0
local r2 = args.bl or 0
local r3 = args.br or 0
local r4 = args.tr or 0
return function(cr, width, height)
cr:new_sub_path()
cr:arc(width - r1, r1, r1, rad(-90), rad(0))
cr:arc(width - r2, height - r2, r2, rad(0), rad(90))
cr:arc(r3, height - r3, r3, rad(90), rad(180))
cr:arc(r4, r4, r4, rad(180), rad(270))
cr:close_path()
end
end
-- Returns a circle of the specified size filled with the specified color
local function circle_filled(color, size)
color = color or "#fefefa"
local surface = cairo.ImageSurface.create("ARGB32", size, size)
local cr = cairo.Context.create(surface)
cr:arc(size / 2, size / 2, size / 2, rad(0), rad(360))
cr:set_source_rgba(hex2rgb(color))
cr.antialias = cairo.Antialias.BEST
cr:fill()
-- cr:arc(
-- size / 2, size / 2, size / 2 - 0.5, rad(135), rad(270))
-- cr:set_source_rgba(hex2rgb(darken(color, 25)))
-- cr.line_width = 1
-- cr:stroke()
return surface
end
-- Returns a vertical gradient pattern going from cololr_1 -> color_2
local function duotone_gradient_vertical(color_1, color_2, height, offset_1,
offset_2)
local fill_pattern = cairo.Pattern.create_linear(0, 0, 0, height)
local r, g, b, a
r, g, b, a = hex2rgb(color_1)
fill_pattern:add_color_stop_rgba(offset_1 or 0, r, g, b, a)
r, g, b, a = hex2rgb(color_2)
fill_pattern:add_color_stop_rgba(offset_2 or 1, r, g, b, a)
return fill_pattern
end
-- Returns a horizontal gradient pattern going from cololr_1 -> color_2
local function duotone_gradient_horizontal(color, width)
local fill_pattern = cairo.Pattern.create_linear(0, 0, width, 0)
local r, g, b, a
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0.5, r, g, b, a)
r, g, b, a = hex2rgb("#00000000")
fill_pattern:add_color_stop_rgba(0.6, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(0.7, r, g, b, a)
r, g, b, a = hex2rgb(color)
fill_pattern:add_color_stop_rgba(1, r, g, b, a)
return fill_pattern
end
-- Flips the given surface around the specified axis
local function flip(surface, axis)
local width = surface:get_width()
local height = surface:get_height()
local flipped = cairo.ImageSurface.create("ARGB32", width, height)
local cr = cairo.Context.create(flipped)
local source_pattern = cairo.Pattern.create_for_surface(surface)
if axis == "horizontal" then
source_pattern.matrix = cairo.Matrix {xx = -1, yy = 1, x0 = width}
elseif axis == "vertical" then
source_pattern.matrix = cairo.Matrix {xx = 1, yy = -1, y0 = height}
elseif axis == "both" then
source_pattern.matrix = cairo.Matrix {
xx = -1,
yy = -1,
x0 = width,
y0 = height,
}
end
cr.source = source_pattern
cr:rectangle(0, 0, width, height)
cr:paint()
return flipped
end
-- Draws the left corner of the titlebar
local function create_corner_top_left(args)
local radius = args.radius
local height = args.height
local surface = cairo.ImageSurface.create("ARGB32", radius, height)
local cr = cairo.Context.create(surface)
-- Create the corner shape and fill it with a gradient
local radius_offset = 1 -- To soften the corner
cr:move_to(0, height)
cr:line_to(0, radius - radius_offset)
cr:arc(
radius + radius_offset, radius + radius_offset, radius, rad(180),
rad(270))
cr:line_to(radius, height)
cr:close_path()
cr.source = args.background_source
cr.antialias = cairo.Antialias.BEST
cr:fill()
-- Next add the subtle 3D look
local function add_stroke(nargs)
local arc_radius = nargs.radius
local offset_x = nargs.offset_x
local offset_y = nargs.offset_y
cr:new_sub_path()
cr:move_to(offset_x, height)
cr:line_to(offset_x, arc_radius + offset_y)
cr:arc(
arc_radius + offset_x, arc_radius + offset_y, arc_radius, rad(180),
rad(270))
cr.source = nargs.source
cr.line_width = nargs.width
cr.antialias = cairo.Antialias.BEST
cr:stroke()
end
-- Outer dark stroke
add_stroke {
offset_x = args.stroke_offset_outer,
offset_y = args.stroke_offset_outer,
radius = radius + 0.5,
source = args.stroke_source_outer,
width = args.stroke_width_outer,
}
-- Inner light stroke
add_stroke {
offset_x = args.stroke_offset_inner,
offset_y = args.stroke_offset_inner,
radius = radius,
width = args.stroke_width_inner,
source = args.stroke_source_inner,
}
return surface
end
-- Draws the middle of the titlebar
local function create_edge_top_middle(args)
local client_color = args.color
local height = args.height
local width = args.width
local surface = cairo.ImageSurface.create("ARGB32", width, height)
local cr = cairo.Context.create(surface)
-- Create the background shape and fill it with a gradient
cr:rectangle(0, 0, width, height)
cr.source = args.background_source
cr:fill()
-- Then add the light and dark strokes for that 3D look
local function add_stroke(stroke_width, stroke_offset, stroke_color)
cr:new_sub_path()
cr:move_to(0, stroke_offset)
cr:line_to(width, stroke_offset)
cr.line_width = stroke_width
cr:set_source_rgb(hex2rgb(stroke_color))
cr:stroke()
end
-- Inner light stroke
add_stroke(
args.stroke_width_inner, args.stroke_offset_inner,
args.stroke_color_inner)
-- Outer dark stroke
add_stroke(
args.stroke_width_outer, args.stroke_offset_outer,
args.stroke_color_outer)
return surface
end
local function create_edge_left(args)
local height = args.height
local width = 2
-- height = height or 1080
local surface = cairo.ImageSurface.create("ARGB32", width, height)
local cr = cairo.Context.create(surface)
cr:rectangle(0, 0, 2, args.height)
cr:set_source_rgb(hex2rgb(args.client_color))
cr:fill()
-- Inner light stroke
cr:new_sub_path()
cr:move_to(args.stroke_offset_inner, 0) -- 1/5
cr:line_to(args.stroke_offset_inner, height)
cr.line_width = args.stroke_width_inner -- 1.5
cr:set_source_rgb(hex2rgb(args.inner_stroke_color))
cr:stroke()
-- Outer dark stroke
cr:new_sub_path()
cr:move_to(args.stroke_offset_outer, 0)
cr:line_to(args.stroke_offset_outer, height)
cr.line_width = args.stroke_width_outer -- 1
cr:set_source_rgb(hex2rgb(args.stroke_color_outer))
cr:stroke()
return surface
end
return {
rounded_rect = rounded_rect,
circle_filled = circle_filled,
duotone_gradient_vertical = duotone_gradient_vertical,
flip = flip,
create_corner_top_left = create_corner_top_left,
create_edge_top_middle = create_edge_top_middle,
create_edge_left = create_edge_left,
}