From 22d656903563f75678f3634964731ccf93355dfd Mon Sep 17 00:00:00 2001 From: ache Date: Mon, 13 Mar 2017 23:17:19 +0100 Subject: Init commit --- lib/gears/shape.lua | 785 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 785 insertions(+) create mode 100644 lib/gears/shape.lua (limited to 'lib/gears/shape.lua') diff --git a/lib/gears/shape.lua b/lib/gears/shape.lua new file mode 100644 index 0000000..4962d78 --- /dev/null +++ b/lib/gears/shape.lua @@ -0,0 +1,785 @@ +--------------------------------------------------------------------------- +--- Module dedicated to gather common shape painters. +-- +-- It add the concept of "shape" to Awesome. A shape can be applied to a +-- background, a margin, a mask or a drawable shape bounding. +-- +-- The functions exposed by this module always take a context as first +-- parameter followed by the widget and height and additional parameters. +-- +-- The functions provided by this module only create a path in the content. +-- to actually draw the content, use `cr:fill()`, `cr:mask()`, `cr:clip()` or +-- `cr:stroke()` +-- +-- In many case, it is necessary to apply the shape using a transformation +-- such as a rotation. The preferred way to do this is to wrap the function +-- in another function calling `cr:rotate()` (or any other transformation +-- matrix). +-- +-- To specialize a shape where the API doesn't allows extra arguments to be +-- passed, it is possible to wrap the shape function like: +-- +-- local new_shape = function(cr, width, height) +-- gears.shape.rounded_rect(cr, width, height, 2) +-- end +-- +-- Many elements can be shaped. This include: +-- +-- * `client`s (see `gears.surface.apply_shape_bounding`) +-- * `wibox`es (see `wibox.shape`) +-- * All widgets (see `wibox.container.background`) +-- * The progressbar (see `wibox.widget.progressbar.bar_shape`) +-- * The graph (see `wibox.widget.graph.step_shape`) +-- * The checkboxes (see `wibox.widget.checkbox.check_shape`) +-- * Images (see `wibox.widget.imagebox.clip_shape`) +-- * The taglist tags (see `awful.widget.taglist`) +-- * The tasklist clients (see `awful.widget.tasklist`) +-- * The tooltips (see `awful.tooltip`) +-- +-- @author Emmanuel Lepage Vallee +-- @copyright 2011-2016 Emmanuel Lepage Vallee +-- @module gears.shape +--------------------------------------------------------------------------- +local g_matrix = require( "gears.matrix" ) +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) +local atan2 = math.atan2 or math.atan -- lua 5.3 compat + +local module = {} + +--- Add a rounded rectangle to the current path. +-- Note: If the radius is bigger than either half side, it will be reduced. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_rounded_rect.svg) +-- +-- @usage +--shape.rounded_rect(cr, 70, 70, 10) +--shape.rounded_rect(cr,20,70, 5) +--shape.transform(shape.rounded_rect) : translate(0,25) (cr,70,20, 5) +-- +-- @param cr A cairo content +-- @tparam number width The rectangle width +-- @tparam number height The rectangle height +-- @tparam number radius the corner radius +function module.rounded_rect(cr, width, height, radius) + + radius = radius or 10 + + if width / 2 < radius then + radius = width / 2 + end + + if height / 2 < radius then + radius = height / 2 + end + + cr:move_to(0, radius) + + cr:arc( radius , radius , radius, math.pi , 3*(math.pi/2) ) + cr:arc( width-radius, radius , radius, 3*(math.pi/2), math.pi*2 ) + cr:arc( width-radius, height-radius, radius, math.pi*2 , math.pi/2 ) + cr:arc( radius , height-radius, radius, math.pi/2 , math.pi ) + + cr:close_path() +end + +--- Add a rectangle delimited by 2 180 degree arcs to the path. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_rounded_bar.svg) +-- +-- @usage +--shape.rounded_bar(cr, 70, 70) +--shape.rounded_bar(cr, 20, 70) +--shape.rounded_bar(cr, 70, 20) +-- +-- @param cr A cairo content +-- @param width The rectangle width +-- @param height The rectangle height +function module.rounded_bar(cr, width, height) + module.rounded_rect(cr, width, height, height / 2) +end + +--- A rounded rect with only some of the corners rounded. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_partially_rounded_rect.svg) +-- +-- @usage +--shape.partially_rounded_rect(cr, 70, 70) +--shape.partially_rounded_rect(cr, 70, 70, true) +--shape.partially_rounded_rect(cr, 70, 70, true, true, false, true, 30) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam boolean tl If the top left corner is rounded +-- @tparam boolean tr If the top right corner is rounded +-- @tparam boolean br If the bottom right corner is rounded +-- @tparam boolean bl If the bottom left corner is rounded +-- @tparam number rad The corner radius +function module.partially_rounded_rect(cr, width, height, tl, tr, br, bl, rad) + rad = rad or 10 + if width / 2 < rad then + rad = width / 2 + end + + if height / 2 < rad then + rad = height / 2 + end + + -- Top left + if tl then + cr:arc( rad, rad, rad, math.pi, 3*(math.pi/2)) + else + cr:move_to(0,0) + end + + -- Top right + if tr then + cr:arc( width-rad, rad, rad, 3*(math.pi/2), math.pi*2) + else + cr:line_to(width, 0) + end + + -- Bottom right + if br then + cr:arc( width-rad, height-rad, rad, math.pi*2 , math.pi/2) + else + cr:line_to(width, height) + end + + -- Bottom left + if bl then + cr:arc( rad, height-rad, rad, math.pi/2, math.pi) + else + cr:line_to(0, height) + end + + cr:close_path() +end + +--- A rounded rectangle with a triangle at the top. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_infobubble.svg) +-- +-- @usage +--shape.infobubble(cr, 70, 70) +--shape.transform(shape.infobubble) : translate(0, 20) +-- : rotate_at(35,35,math.pi) (cr,70,20,10, 5, 35 - 5) +--shape.transform(shape.infobubble) +-- : rotate_at(35,35,3*math.pi/2) (cr,70,70, nil, nil, 40) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=5] number corner_radius The corner radius +-- @tparam[opt=10] number arrow_size The width and height of the arrow +-- @tparam[opt=width/2 - arrow_size/2] number arrow_position The position of the arrow +function module.infobubble(cr, width, height, corner_radius, arrow_size, arrow_position) + arrow_size = arrow_size or 10 + corner_radius = math.min((height-arrow_size)/2, corner_radius or 5) + arrow_position = arrow_position or width/2 - arrow_size/2 + + + cr:move_to(0 ,corner_radius+arrow_size) + + -- Top left corner + cr:arc(corner_radius, corner_radius+arrow_size, (corner_radius), math.pi, 3*(math.pi/2)) + + -- The arrow triangle (still at the top) + cr:line_to(arrow_position , arrow_size ) + cr:line_to(arrow_position + arrow_size , 0 ) + cr:line_to(arrow_position + 2*arrow_size , arrow_size ) + + -- Complete the rounded rounded rectangle + cr:arc(width-corner_radius, corner_radius+arrow_size , (corner_radius) , 3*(math.pi/2) , math.pi*2 ) + cr:arc(width-corner_radius, height-(corner_radius) , (corner_radius) , math.pi*2 , math.pi/2 ) + cr:arc(corner_radius , height-(corner_radius) , (corner_radius) , math.pi/2 , math.pi ) + + -- Close path + cr:close_path() +end + +--- A rectangle terminated by an arrow. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_rectangular_tag.svg) +-- +-- @usage +--shape.rectangular_tag(cr, 70, 70) +--shape.transform(shape.rectangular_tag) : translate(0, 30) (cr, 70, 10, 10) +--shape.transform(shape.rectangular_tag) : translate(0, 30) (cr, 70, 10, -10) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=height/2] number arrow_length The length of the arrow part +function module.rectangular_tag(cr, width, height, arrow_length) + arrow_length = arrow_length or height/2 + if arrow_length > 0 then + cr:move_to(0 , height/2 ) + cr:line_to(arrow_length , 0 ) + cr:line_to(width , 0 ) + cr:line_to(width , height ) + cr:line_to(arrow_length , height ) + else + cr:move_to(0 , 0 ) + cr:line_to(-arrow_length, height/2 ) + cr:line_to(0 , height ) + cr:line_to(width , height ) + cr:line_to(width , 0 ) + end + + cr:close_path() +end + +--- A simple arrow shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_arrow.svg) +-- +-- @usage +--shape.arrow(cr, 70, 70) +--shape.arrow(cr,70,70, 30, 10, 60) +--shape.transform(shape.arrow) : rotate_at(35,35,math.pi/2)(cr,70,70) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=head_width] number head_width The width of the head (/\) of the arrow +-- @tparam[opt=width /2] number shaft_width The width of the shaft of the arrow +-- @tparam[opt=height/2] number shaft_length The head_length of the shaft (the rest is the head) +function module.arrow(cr, width, height, head_width, shaft_width, shaft_length) + shaft_length = shaft_length or height / 2 + shaft_width = shaft_width or width / 2 + head_width = head_width or width + local head_length = height - shaft_length + + cr:move_to ( width/2 , 0 ) + cr:rel_line_to( head_width/2 , head_length ) + cr:rel_line_to( -(head_width-shaft_width)/2 , 0 ) + cr:rel_line_to( 0 , shaft_length ) + cr:rel_line_to( -shaft_width , 0 ) + cr:rel_line_to( 0 , -shaft_length ) + cr:rel_line_to( -(head_width-shaft_width)/2 , 0 ) + + cr:close_path() +end + +--- A squeezed hexagon filling the rectangle. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_hexagon.svg) +-- +-- @usage +--shape.hexagon(cr, 70, 70) +--shape.transform(shape.hexagon) : translate(0,15)(cr,70,20) +--shape.transform(shape.hexagon) : rotate_at(35,35,math.pi/2)(cr,70,40) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +function module.hexagon(cr, width, height) + cr:move_to(height/2,0) + cr:line_to(width-height/2,0) + cr:line_to(width,height/2) + cr:line_to(width-height/2,height) + cr:line_to(height/2,height) + cr:line_to(0,height/2) + cr:line_to(height/2,0) + cr:close_path() +end + +--- Double arrow popularized by the vim-powerline module. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_powerline.svg) +-- +-- @usage +--shape.powerline(cr, 70, 70) +--shape.transform(shape.powerline) : translate(0, 25) (cr,70,20) +--shape.transform(shape.powerline) : translate(0, 25) (cr,70,20, -20) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=height/2] number arrow_depth The width of the arrow part of the shape +function module.powerline(cr, width, height, arrow_depth) + arrow_depth = arrow_depth or height/2 + local offset = 0 + + -- Avoid going out of the (potential) clip area + if arrow_depth < 0 then + width = width + 2*arrow_depth + offset = -arrow_depth + end + + cr:move_to(offset , 0 ) + cr:line_to(offset + width - arrow_depth , 0 ) + cr:line_to(offset + width , height/2 ) + cr:line_to(offset + width - arrow_depth , height ) + cr:line_to(offset , height ) + cr:line_to(offset + arrow_depth , height/2 ) + + cr:close_path() +end + +--- An isosceles triangle. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_isosceles_triangle.svg) +-- +-- @usage +--shape.isosceles_triangle(cr, 70, 70) +--shape.isosceles_triangle(cr,20,70) +--shape.transform(shape.isosceles_triangle) : rotate_at(35, 35, math.pi/2)(cr,70,70) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +function module.isosceles_triangle(cr, width, height) + cr:move_to( width/2, 0 ) + cr:line_to( width , height ) + cr:line_to( 0 , height ) + cr:close_path() +end + +--- A cross (**+**) symbol. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_cross.svg) +-- +-- @usage +--shape.cross(cr, 70, 70) +--shape.cross(cr,20,70) +--shape.transform(shape.cross) : scale(0.5, 1)(cr,70,70) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=width/3] number thickness The cross section thickness +function module.cross(cr, width, height, thickness) + thickness = thickness or width/3 + local xpadding = (width - thickness) / 2 + local ypadding = (height - thickness) / 2 + cr:move_to(xpadding, 0) + cr:line_to(width - xpadding, 0) + cr:line_to(width - xpadding, ypadding) + cr:line_to(width , ypadding) + cr:line_to(width , height-ypadding) + cr:line_to(width - xpadding, height-ypadding) + cr:line_to(width - xpadding, height ) + cr:line_to(xpadding , height ) + cr:line_to(xpadding , height-ypadding) + cr:line_to(0 , height-ypadding) + cr:line_to(0 , ypadding ) + cr:line_to(xpadding , ypadding ) + cr:close_path() +end + +--- A similar shape to the `rounded_rect`, but with sharp corners. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_octogon.svg) +-- +-- @usage +--shape.octogon(cr, 70, 70) +--shape.octogon(cr,70,70,70/2.5) +--shape.transform(shape.octogon) : translate(0, 25) (cr,70,20) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam number corner_radius +function module.octogon(cr, width, height, corner_radius) + corner_radius = corner_radius or math.min(10, math.min(width, height)/4) + local offset = math.sqrt( (corner_radius*corner_radius) / 2 ) + + cr:move_to(offset, 0) + cr:line_to(width-offset, 0) + cr:line_to(width, offset) + cr:line_to(width, height-offset) + cr:line_to(width-offset, height) + cr:line_to(offset, height) + cr:line_to(0, height-offset) + cr:line_to(0, offset) + cr:close_path() +end + +--- A circle shape. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_circle.svg) +-- +-- @usage +--shape.circle(cr, 70, 70) +--shape.circle(cr,20,70) +--shape.transform(shape.circle) : scale(0.5, 1)(cr,70,70) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=math.min(width height) / 2)] number radius The radius +function module.circle(cr, width, height, radius) + radius = radius or math.min(width, height) / 2 + cr:move_to(width/2+radius, height/2) + cr:arc(width / 2, height / 2, radius, 0, 2*math.pi) + cr:close_path() +end + +--- A simple rectangle. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_rectangle.svg) +-- +-- @usage +--shape.rectangle(cr, 70, 70) +--shape.rectangle(cr,20,70) +--shape.transform(shape.rectangle) : scale(0.5, 1)(cr,70,70) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +function module.rectangle(cr, width, height) + cr:rectangle(0, 0, width, height) +end + +--- A diagonal parallelogram with the bottom left corner at x=0 and top right +-- at x=width. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_parallelogram.svg) +-- +-- @usage +--shape.parallelogram(cr, 70, 70) +--shape.parallelogram(cr,70,20) +--shape.transform(shape.parallelogram) : scale(0.5, 1)(cr,70,70) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=width/3] number base_width The parallelogram base width +function module.parallelogram(cr, width, height, base_width) + base_width = base_width or width/3 + cr:move_to(width-base_width, 0 ) + cr:line_to(width , 0 ) + cr:line_to(base_width , height ) + cr:line_to(0 , height ) + cr:close_path() +end + +--- A losange. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_losange.svg) +-- +-- @usage +--shape.losange(cr, 70, 70) +--shape.losange(cr,20,70) +--shape.transform(shape.losange) : scale(0.5, 1)(cr,70,70) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +function module.losange(cr, width, height) + cr:move_to(width/2 , 0 ) + cr:line_to(width , height/2 ) + cr:line_to(width/2 , height ) + cr:line_to(0 , height/2 ) + cr:close_path() +end + +--- A pie. +-- +-- The pie center is the center of the area. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_pie.svg) +-- +-- @usage +--shape.pie(cr, 70, 70) +--shape.pie(cr,70,70, 1.0471975511966, 4.1887902047864) +--shape.pie(cr,70,70, 0, 2*math.pi, 10) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=0] number start_angle The start angle (in radian) +-- @tparam[opt=math.pi/2] number end_angle The end angle (in radian) +-- @tparam[opt=math.min(width height)/2] number radius The shape height +function module.pie(cr, width, height, start_angle, end_angle, radius) + radius = radius or math.floor(math.min(width, height)/2) + start_angle, end_angle = start_angle or 0, end_angle or math.pi/2 + + -- If the shape is a circle, then avoid the lines + if math.abs(start_angle + end_angle - 2*math.pi) <= 0.01 then + cr:arc(width/2, height/2, radius, 0, 2*math.pi) + else + cr:move_to(width/2, height/2) + cr:line_to( + width/2 + math.cos(start_angle)*radius, + height/2 + math.sin(start_angle)*radius + ) + cr:arc(width/2, height/2, radius, start_angle, end_angle) + end + + cr:close_path() +end + +--- A rounded arc. +-- +-- The pie center is the center of the area. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_arc.svg) +-- +-- @usage +--shape.arc(cr,70,70, 10) +--shape.arc(cr,70,70, 10, nil, nil, true, true) +--shape.arc(cr,70,70, nil, 0, 2*math.pi) +-- +-- @param cr A cairo context +-- @tparam number width The shape width +-- @tparam number height The shape height +-- @tparam[opt=math.min(width height)/2] number thickness The arc thickness +-- @tparam[opt=0] number start_angle The start angle (in radian) +-- @tparam[opt=math.pi/2] number end_angle The end angle (in radian) +-- @tparam[opt=false] boolean start_rounded if the arc start rounded +-- @tparam[opt=false] boolean end_rounded if the arc end rounded +function module.arc(cr, width, height, thickness, start_angle, end_angle, start_rounded, end_rounded) + start_angle = start_angle or 0 + end_angle = end_angle or math.pi/2 + + -- This shape is a partial circle + local radius = math.min(width, height)/2 + + thickness = thickness or radius/2 + + local inner_radius = radius - thickness + + -- As the edge of the small arc need to touch the [start_p1, start_p2] + -- line, a small subset of the arc circumference has to be substracted + -- that's (less or more) equal to the thickness/2 (a little longer given + -- it is an arc and not a line, but it wont show) + local arc_percent = math.abs(end_angle-start_angle)/(2*math.pi) + local arc_length = ((radius-thickness/2)*2*math.pi)*arc_percent + + if start_rounded then + arc_length = arc_length - thickness/2 + + -- And back to angles + start_angle = end_angle - (arc_length/(radius - thickness/2)) + end + + if end_rounded then + arc_length = arc_length - thickness/2 + + -- And back to angles + end_angle = start_angle + (arc_length/(radius - thickness/2)) + end + + -- The path is a curcular arc joining 4 points + + -- Outer first corner + local start_p1 = { + width /2 + math.cos(start_angle)*radius, + height/2 + math.sin(start_angle)*radius + } + + if start_rounded then + + -- Inner first corner + local start_p2 = { + width /2 + math.cos(start_angle)*inner_radius, + height/2 + math.sin(start_angle)*inner_radius + } + + local median_angle = atan2( + start_p2[1] - start_p1[1], + -(start_p2[2] - start_p1[2]) + ) + + local arc_center = { + (start_p1[1] + start_p2[1])/2, + (start_p1[2] + start_p2[2])/2, + } + + cr:arc(arc_center[1], arc_center[2], thickness/2, + median_angle-math.pi/2, median_angle+math.pi/2 + ) + + else + cr:move_to(unpack(start_p1)) + end + + cr:arc(width/2, height/2, radius, start_angle, end_angle) + + if end_rounded then + + -- Outer second corner + local end_p1 = { + width /2 + math.cos(end_angle)*radius, + height/2 + math.sin(end_angle)*radius + } + + -- Inner first corner + local end_p2 = { + width /2 + math.cos(end_angle)*inner_radius, + height/2 + math.sin(end_angle)*inner_radius + } + local median_angle = atan2( + end_p2[1] - end_p1[1], + -(end_p2[2] - end_p1[2]) + ) - math.pi + + local arc_center = { + (end_p1[1] + end_p2[1])/2, + (end_p1[2] + end_p2[2])/2, + } + + cr:arc(arc_center[1], arc_center[2], thickness/2, + median_angle-math.pi/2, median_angle+math.pi/2 + ) + + end + + cr:arc_negative(width/2, height/2, inner_radius, end_angle, start_angle) + + cr:close_path() +end + +--- A partial rounded bar. How much of the rounded bar is visible depends on +-- the given percentage value. +-- +-- Note that this shape is not closed and thus filling it doesn't make much +-- sense. +-- +-- +-- +--![Usage example](../images/AUTOGEN_gears_shape_radial_progress.svg) +-- +-- @usage +--shape.radial_progress(cr, 70, 20, .3) +--shape.radial_progress(cr, 70, 20, .6) +--shape.radial_progress(cr, 70, 20, .9) +-- +-- @param cr A cairo context +-- @tparam number w The shape width +-- @tparam number h The shape height +-- @tparam number percent The progressbar percent +-- @tparam boolean hide_left Do not draw the left side of the shape +function module.radial_progress(cr, w, h, percent, hide_left) + percent = percent or 1 + local total_length = (2*(w-h))+2*((h/2)*math.pi) + local bar_percent = (w-h)/total_length + local arc_percent = ((h/2)*math.pi)/total_length + + -- Bottom line + if percent > bar_percent then + cr:move_to(h/2,h) + cr:line_to((h/2) + (w-h),h) + cr:stroke() + elseif percent < bar_percent then + cr:move_to(h/2,h) + cr:line_to(h/2+(total_length*percent),h) + cr:stroke() + end + + -- Right arc + if percent >= bar_percent+arc_percent then + cr:arc(w-h/2 , h/2, h/2,3*(math.pi/2),math.pi/2) + cr:stroke() + elseif percent > bar_percent and percent < bar_percent+(arc_percent/2) then + cr:arc(w-h/2 , h/2, h/2,(math.pi/2)-((math.pi/2)*((percent-bar_percent)/(arc_percent/2))),math.pi/2) + cr:stroke() + elseif percent >= bar_percent+arc_percent/2 and percent < bar_percent+arc_percent then + cr:arc(w-h/2 , h/2, h/2,0,math.pi/2) + cr:stroke() + local add = (math.pi/2)*((percent-bar_percent-arc_percent/2)/(arc_percent/2)) + cr:arc(w-h/2 , h/2, h/2,2*math.pi-add,0) + cr:stroke() + end + + -- Top line + if percent > 2*bar_percent+arc_percent then + cr:move_to((h/2) + (w-h),0) + cr:line_to(h/2,0) + cr:stroke() + elseif percent > bar_percent+arc_percent and percent < 2*bar_percent+arc_percent then + cr:move_to((h/2) + (w-h),0) + cr:line_to(((h/2) + (w-h))-total_length*(percent-bar_percent-arc_percent),0) + cr:stroke() + end + + -- Left arc + if not hide_left then + if percent > 0.985 then + cr:arc(h/2, h/2, h/2,math.pi/2,3*(math.pi/2)) + cr:stroke() + elseif percent > 2*bar_percent+arc_percent then + local relpercent = (percent - 2*bar_percent - arc_percent)/arc_percent + cr:arc(h/2, h/2, h/2,3*(math.pi/2)-(math.pi)*relpercent,3*(math.pi/2)) + cr:stroke() + end + end +end + +--- Adjust the shape using a transformation object +-- +-- Apply various transformations to the shape +-- +-- @usage gears.shape.transform(gears.shape.rounded_bar) +-- : rotate(math.pi/2) +-- : translate(10, 10) +-- +-- @param shape A shape function +-- @return A transformation handle, also act as a shape function +function module.transform(shape) + + -- Apply the transformation matrix and apply the shape, then restore + local function apply(self, cr, width, height, ...) + cr:save() + cr:transform(self.matrix:to_cairo_matrix()) + shape(cr, width, height, ...) + cr:restore() + end + -- Redirect function calls like :rotate() to the underlying matrix + local function index(_, key) + return function(self, ...) + self.matrix = self.matrix[key](self.matrix, ...) + return self + end + end + + local result = setmetatable({ + matrix = g_matrix.identity + }, { + __call = apply, + __index = index + }) + + return result +end + +return module + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 -- cgit v1.2.3-54-g00ecf