Beispiel #1
0
    def _draw_transparent_background(self, context, lod):
        """ fill with the transparent background color """
        corner_radius = config.CORNER_RADIUS
        rect = self.get_keyboard_frame_rect()
        fill = self.get_background_rgba()

        fill_gradient = config.theme_settings.background_gradient
        if lod == LOD.MINIMAL or \
           fill_gradient == 0:
            context.set_source_rgba(*fill)
        else:
            fill_gradient /= 100.0
            direction = config.theme_settings.key_gradient_direction
            alpha = -pi / 2.0 + pi * direction / 180.0
            gline = gradient_line(rect, alpha)

            pat = cairo.LinearGradient(*gline)
            rgba = brighten(+fill_gradient * .5, *fill)
            pat.add_color_stop_rgba(0, *rgba)
            rgba = brighten(-fill_gradient * .5, *fill)
            pat.add_color_stop_rgba(1, *rgba)
            context.set_source(pat)

        frame = self.can_draw_frame()
        if frame:
            roundrect_arc(context, rect, corner_radius)
        else:
            context.rectangle(*rect)
        context.fill()

        if frame:
            self.draw_window_frame(context, lod)
            self.draw_keyboard_frame(context, lod)
Beispiel #2
0
    def _draw_transparent_background(self, context, lod):
        """ fill with the transparent background color """
        corner_radius = config.CORNER_RADIUS
        rect = self.get_keyboard_frame_rect()
        fill = self.get_background_rgba()

        fill_gradient = config.theme_settings.background_gradient
        if lod == LOD.MINIMAL or \
           fill_gradient == 0:
            context.set_source_rgba(*fill)
        else:
            fill_gradient /= 100.0
            direction = config.theme_settings.key_gradient_direction
            alpha = -pi/2.0 + pi * direction / 180.0
            gline = gradient_line(rect, alpha)

            pat = cairo.LinearGradient (*gline)
            rgba = brighten(+fill_gradient*.5, *fill)
            pat.add_color_stop_rgba(0, *rgba)
            rgba = brighten(-fill_gradient*.5, *fill)
            pat.add_color_stop_rgba(1, *rgba)
            context.set_source (pat)

        frame = self.can_draw_frame()
        if frame:
            roundrect_arc(context, rect, corner_radius)
        else:
            context.rectangle(*rect)
        context.fill()

        if frame:
            self.draw_window_frame(context, lod)
            self.draw_keyboard_frame(context, lod)
Beispiel #3
0
    def draw_gradient_key(self, cr, fill, line_width, lod):
        # simple gradients for fill and stroke
        fill_gradient = config.theme_settings.key_fill_gradient / 100.0
        stroke_gradient = self.get_stroke_gradient()
        alpha = self.get_gradient_angle()

        rect = self.get_canvas_rect()
        self._build_canvas_path(cr, rect)
        gline = gradient_line(rect, alpha)

        # fill
        if self.show_face:
            if fill_gradient and lod:
                pat = cairo.LinearGradient(*gline)
                rgba = brighten(+fill_gradient * .5, *fill)
                pat.add_color_stop_rgba(0, *rgba)
                rgba = brighten(-fill_gradient * .5, *fill)
                pat.add_color_stop_rgba(1, *rgba)
                cr.set_source(pat)
            else:  # take gradient from color scheme (not implemented)
                cr.set_source_rgba(*fill)

            if self.show_border:
                cr.fill_preserve()
            else:
                cr.fill()

        # stroke
        if self.show_border:
            if stroke_gradient:
                if lod:
                    stroke = fill
                    pat = cairo.LinearGradient(*gline)
                    rgba = brighten(+stroke_gradient * .5, *stroke)
                    pat.add_color_stop_rgba(0, *rgba)
                    rgba = brighten(-stroke_gradient * .5, *stroke)
                    pat.add_color_stop_rgba(1, *rgba)
                    cr.set_source(pat)
                else:
                    cr.set_source_rgba(*fill)
            else:
                cr.set_source_rgba(*self.get_stroke_color())

            cr.set_line_width(line_width)
        cr.stroke()
Beispiel #4
0
    def draw_gradient_key(self, cr, fill, line_width, lod):
        # simple gradients for fill and stroke
        fill_gradient   = config.theme_settings.key_fill_gradient / 100.0
        stroke_gradient = self.get_stroke_gradient()
        alpha = self.get_gradient_angle()

        rect = self.get_canvas_rect()
        self._build_canvas_path(cr, rect)
        gline = gradient_line(rect, alpha)

        # fill
        if self.show_face:
            if fill_gradient and lod:
                pat = cairo.LinearGradient (*gline)
                rgba = brighten(+fill_gradient*.5, *fill)
                pat.add_color_stop_rgba(0, *rgba)
                rgba = brighten(-fill_gradient*.5, *fill)
                pat.add_color_stop_rgba(1, *rgba)
                cr.set_source (pat)
            else: # take gradient from color scheme (not implemented)
                cr.set_source_rgba(*fill)

            if self.show_border:
                cr.fill_preserve()
            else:
                cr.fill()

        # stroke
        if self.show_border:
            if stroke_gradient:
                if lod:
                    stroke = fill
                    pat = cairo.LinearGradient (*gline)
                    rgba = brighten(+stroke_gradient*.5, *stroke)
                    pat.add_color_stop_rgba(0, *rgba)
                    rgba = brighten(-stroke_gradient*.5, *stroke)
                    pat.add_color_stop_rgba(1, *rgba)
                    cr.set_source (pat)
                else:
                    cr.set_source_rgba(*fill)
            else:
                cr.set_source_rgba(*self.get_stroke_color())

            cr.set_line_width(line_width)
        cr.stroke()
Beispiel #5
0
    def _get_insensitive_color(self, key, state, element):
        new_state = state.copy()
        new_state["insensitive"] = False
        fill = self.get_key_rgba(key, "fill", new_state)
        rgba = self.get_key_rgba(key, element, new_state)

        h, lf, s = colorsys.rgb_to_hls(*fill[:3])
        h, ll, s = colorsys.rgb_to_hls(*rgba[:3])

        # Leave only one third of the lightness difference
        # between label and fill color.
        amount = (ll - lf) * 2.0 / 3.0
        return brighten(-amount, *rgba)
Beispiel #6
0
    def _get_insensitive_color(self, key, state, element):
        new_state = state.copy()
        new_state["insensitive"] = False
        fill = self.get_key_rgba(key, "fill", new_state)
        rgba = self.get_key_rgba(key, element, new_state)

        h, lf, s = colorsys.rgb_to_hls(*fill[:3])
        h, ll, s = colorsys.rgb_to_hls(*rgba[:3])

        # Leave only one third of the lightness difference
        # between label and fill color.
        amount = (ll - lf) * 2.0 / 3.0
        return brighten(-amount, *rgba)
Beispiel #7
0
    def _label_iterations(self, src_size, log_rect):
        canvas_rect = self.context.log_to_canvas_rect(log_rect)
        xoffset, yoffset = self.align_label(
                 (src_size[0], src_size[1]),
                 (canvas_rect.w, canvas_rect.h))
        x = int(canvas_rect.x + xoffset)
        y = int(canvas_rect.y + yoffset)

        stroke_gradient   = config.theme_settings.key_stroke_gradient / 100.0
        if config.theme_settings.key_style != "flat" and stroke_gradient:
            root = self.get_layout_root()
            fill = self.get_fill_color()
            d = 0.4  # fake emboss distance
            #d = max(src_size[1] * 0.02, 0.0)
            max_offset = 2

            # shadow
            alpha = self.get_gradient_angle()
            xo = root.context.scale_log_to_canvas_x(d * cos(alpha))
            yo = root.context.scale_log_to_canvas_y(d * sin(alpha))
            xo = min(int(round(xo)), max_offset)
            yo = min(int(round(yo)), max_offset)
            rgba = brighten(-stroke_gradient*.25, *fill) # darker
            yield x + xo, y + yo, rgba, False

            # highlight
            alpha = pi + self.get_gradient_angle()
            xo = root.context.scale_log_to_canvas_x(d * cos(alpha))
            yo = root.context.scale_log_to_canvas_y(d * sin(alpha))
            xo = min(int(round(xo)), max_offset)
            yo = min(int(round(yo)), max_offset)
            rgba = brighten(+stroke_gradient*.25, *fill) # brighter
            yield x + xo, y + yo, rgba, False

        # normal
        rgba = self.get_label_color()
        yield x, y, rgba, True
Beispiel #8
0
    def draw_image(self, context, lod):
        """
        Draws the key's optional image.
        Fixme: merge with draw_label, can't do this for 0.99 because
        the Gdk.flush() workaround on the nexus 7 might fail.
        """
        if not self.image_filenames:
            return

        log_rect = self.get_label_rect()
        rect = self.context.log_to_canvas_rect(log_rect)
        if rect.w < 1 or rect.h < 1:
            return

        pixbuf = self.get_image(rect.w, rect.h)
        if not pixbuf:
            return

        src_size = (pixbuf.get_width(), pixbuf.get_height())
        log_rect = self.get_label_rect()

        canvas_rect = self.context.log_to_canvas_rect(log_rect)
        xalign, yalign = self.align_label(src_size,
                                          (canvas_rect.w, canvas_rect.h))
        x = int(canvas_rect.x + xalign)
        y = int(canvas_rect.y + yalign)

        label_rgba = self.get_label_color()
        fill = self.get_fill_color()

        for dx, dy, lum, last in self._label_iterations(lod):
            # draw dwell progress after fake emboss, before final image
            if last and self.is_dwelling():
                DwellProgress.draw(self, context,
                                   self.get_dwell_progress_canvas_rect(),
                                   self.get_dwell_progress_color())
            if lum:
                rgba = brighten(lum, *fill)  # darker
            else:
                rgba = label_rgba

            # Draw the image in the themes label color.
            # Only the alpha channel of the image is used.
            Gdk.cairo_set_source_pixbuf(context, pixbuf, x + dx, y + dy)
            pattern = context.get_source()
            context.rectangle(*rect)
            context.set_source_rgba(*rgba)
            context.mask(pattern)
            context.new_path()
Beispiel #9
0
    def draw_gradient_key(self, context, rect, fill, line_width):
        # simple gradients for fill and stroke
        fill_gradient   = config.theme_settings.key_fill_gradient / 100.0
        stroke_gradient = config.theme_settings.key_stroke_gradient / 100.0
        alpha = self.get_gradient_angle()

        self.build_rect_path(context, rect)
        gline = self.get_gradient_line(rect, alpha)

        # fill
        if fill_gradient:
            pat = cairo.LinearGradient (*gline)
            rgba = brighten(+fill_gradient*.5, *fill)
            pat.add_color_stop_rgba(0, *rgba)
            rgba = brighten(-fill_gradient*.5, *fill)
            pat.add_color_stop_rgba(1, *rgba)
            context.set_source (pat)
        else: # take gradient from color scheme (not implemented)
            context.set_source_rgba(*fill)

        context.fill_preserve()

        # stroke
        if stroke_gradient:
            stroke = fill
            pat = cairo.LinearGradient (*gline)
            rgba = brighten(+stroke_gradient*.5, *stroke)
            pat.add_color_stop_rgba(0, *rgba)
            rgba = brighten(-stroke_gradient*.5, *stroke)
            pat.add_color_stop_rgba(1, *rgba)
            context.set_source (pat)
        else:
            context.set_source_rgba(*self.get_stroke_color())

        context.set_line_width(line_width)
        context.stroke()
Beispiel #10
0
    def draw_image(self, context, lod):
        """
        Draws the key's optional image.
        Fixme: merge with draw_label, can't do this for 0.99 because
        the Gdk.flush() workaround on the nexus 7 might fail.
        """
        if not self.image_filenames:
            return

        log_rect = self.get_label_rect()
        rect = self.context.log_to_canvas_rect(log_rect)
        if rect.w < 1 or rect.h < 1:
            return

        pixbuf = self.get_image(rect.w, rect.h)
        if not pixbuf:
            return

        src_size = (pixbuf.get_width(), pixbuf.get_height())
        log_rect = self.get_label_rect()

        canvas_rect = self.context.log_to_canvas_rect(log_rect)
        xalign, yalign = self.align_label(src_size, (canvas_rect.w, canvas_rect.h))
        x = int(canvas_rect.x + xalign)
        y = int(canvas_rect.y + yalign)

        label_rgba = self.get_label_color()
        fill = self.get_fill_color()

        for dx, dy, lum, last in self._label_iterations(lod):
            # draw dwell progress after fake emboss, before final image
            if last and self.is_dwelling():
                DwellProgress.draw(
                    self, context, self.get_dwell_progress_canvas_rect(), self.get_dwell_progress_color()
                )
            if lum:
                rgba = brighten(lum, *fill)  # darker
            else:
                rgba = label_rgba

            # Draw the image in the themes label color.
            # Only the alpha channel of the image is used.
            Gdk.cairo_set_source_pixbuf(context, pixbuf, x + dx, y + dy)
            pattern = context.get_source()
            context.rectangle(*rect)
            context.set_source_rgba(*rgba)
            context.mask(pattern)
            context.new_path()
Beispiel #11
0
    def draw_image(self, context, lod):
        """
        Draws the key's optional image.
        Fixme: merge with draw_label, can't do this for 0.99 because
        the Gdk.flush() workaround on the nexus 7 might fail.
        """
        if not self.image_filenames or not self.show_image:
            return

        log_rect = self.get_label_rect()
        rect = self.context.log_to_canvas_rect(log_rect)
        if rect.w < 1 or rect.h < 1:
            return

        pixbuf = self.get_image(rect.w, rect.h)
        if not pixbuf:
            return

        src_size = (pixbuf.get_width(), pixbuf.get_height())
        xalign, yalign = self.align_label(src_size, (rect.w, rect.h))

        label_rgba = self.get_label_color()
        fill = self.get_fill_color()

        for dx, dy, lum, last in self._label_iterations(lod):
            # draw dwell progress after fake emboss, before final image
            if last and self.is_dwelling():
                DwellProgress.draw(self, context,
                                   self.get_dwell_progress_canvas_rect(),
                                   self.get_dwell_progress_color())
            if lum:
                rgba = brighten(lum, *fill) # darker
            else:
                rgba = label_rgba

            pixbuf.draw(context, rect.offset(xalign + dx, yalign + dy), rgba)
Beispiel #12
0
    def draw_label(self, context, lod):
        # Skip cairo errors when drawing labels with font size 0
        # This may happen for hidden keys and keys with bad size groups.
        if self.font_size == 0:
            return

        runs = self.get_label_runs()
        if not runs:
            return

        fill = self.get_fill_color()

        for dx, dy, lum, last in self._label_iterations(lod):
            # draw dwell progress after fake emboss, before final image
            if last and self.is_dwelling():
                DwellProgress.draw(self, context,
                                   self.get_dwell_progress_canvas_rect(),
                                   self.get_dwell_progress_color())
            for layout, x, y, rgba in runs:
                if lum:
                    rgba = brighten(lum, *fill)  # darker
                context.move_to(x + dx, y + dy)
                context.set_source_rgba(*rgba)
                PangoCairo.show_layout(context, layout)
Beispiel #13
0
    def draw_label(self, context, lod):
        # Skip cairo errors when drawing labels with font size 0
        # This may happen for hidden keys and keys with bad size groups.
        if self.font_size == 0 or not self.show_label:
            return

        runs = self.get_label_runs()
        if not runs:
            return

        fill = self.get_fill_color()

        for dx, dy, lum, last in self._label_iterations(lod):
            # draw dwell progress after fake emboss, before final image
            if last and self.is_dwelling():
                DwellProgress.draw(self, context,
                                   self.get_dwell_progress_canvas_rect(),
                                   self.get_dwell_progress_color())
            for layout, x, y, rgba in runs:
                if lum:
                    rgba = brighten(lum, *fill) # darker
                context.move_to(x + dx, y + dy)
                context.set_source_rgba(*rgba)
                PangoCairo.show_layout(context, layout)
Beispiel #14
0
    def draw_image(self, context, lod):
        """
        Draws the key's optional image.
        Fixme: merge with draw_label, can't do this for 0.99 because
        the Gdk.flush() workaround on the nexus 7 might fail.
        """
        if not self.image_filenames or not self.show_image:
            return

        log_rect = self.get_label_rect()
        rect = self.context.log_to_canvas_rect(log_rect)
        if rect.w < 1 or rect.h < 1:
            return

        pixbuf = self.get_image(rect.w, rect.h)
        if not pixbuf:
            return

        src_size = (pixbuf.get_width(), pixbuf.get_height())
        xalign, yalign = self.align_label(src_size, (rect.w, rect.h))

        label_rgba = self.get_label_color()
        fill = self.get_fill_color()

        for dx, dy, lum, last in self._label_iterations(lod):
            # draw dwell progress after fake emboss, before final image
            if last and self.is_dwelling():
                DwellProgress.draw(self, context,
                                   self.get_dwell_progress_canvas_rect(),
                                   self.get_dwell_progress_color())
            if lum:
                rgba = brighten(lum, *fill)  # darker
            else:
                rgba = label_rgba

            pixbuf.draw(context, rect.offset(xalign + dx, yalign + dy), rgba)
Beispiel #15
0
    def _draw_dish_key_border(self, cr, path, path_top, polygon, polygon_top,
                              base_rgba, stroke_gradient, lightx, lighty):
        n = len(polygon)
        m = len(path)

        # Lambert lighting
        edge_colors = []
        for i in range(0, n, 2):
            x0 = polygon[i]
            y0 = polygon[i + 1]
            if i < n - 2:
                x1 = polygon[i + 2]
                y1 = polygon[i + 3]
            else:
                x1 = polygon[0]
                y1 = polygon[1]

            nx = y1 - y0
            ny = -(x1 - x0)
            ln = sqrt(nx * nx + ny * ny)
            I = (nx * lightx + ny * lighty) / ln \
                * stroke_gradient * 0.8 \
                if ln else 0.0
            edge_colors.append(brighten(I, *base_rgba))

        # draw border sections
        edge = 0
        for i in range(0, m - 2, 2):
            # get path points
            i1 = i + 1
            i2 = i + 2
            if i2 >= m:
                i2 -= m
            i3 = i + 3
            if i3 >= m:
                i3 = 1

            p0 = path[i]
            p0x = p0[0]
            p0y = p0[1]

            p1 = path[i1]
            p1x = p1[0]
            p1y = p1[1]

            p2 = path[i2]
            p2x = p2[0]
            p2y = p2[1]

            p3 = path[i3]
            p3x = p3[0]
            p3y = p3[1]

            p = path_top[i]
            ptop0x = p[0]
            ptop0y = p[1]

            p = path_top[i1]
            ptop1x = p[0]
            ptop1y = p[1]

            p = path_top[i2]
            ptop2x = p[0]
            ptop2y = p[1]

            # get polygon points, only to
            # fill in gaps at concave corners.
            j0 = edge * 2
            j1 = j0 + 2
            if j1 >= n:
                j1 -= n
            j2 = j0 + 4
            if j2 >= n:
                j2 -= n

            ptopax = polygon_top[j0]
            ptopay = polygon_top[j0 + 1]
            ptopbx = polygon_top[j1]
            ptopby = polygon_top[j1 + 1]
            ptopcx = polygon_top[j2]
            ptopcy = polygon_top[j2 + 1]
            vax = ptopbx - ptopax
            vay = ptopby - ptopay
            nbx = ptopcy - ptopby
            nby = -(ptopcx - ptopbx)
            concave = vax * nbx + vay * nby < 0.0

            # Fake Gouraud shading: draw a gradient between mid points
            # of the lines connecting the base with the top path.
            pat = cairo.LinearGradient(
                (p1x + ptop1x) * 0.5, (p1y + ptop1y) * 0.5,
                (p2x + ptop2x) * 0.5, (p2y + ptop2y) * 0.5)
            edge1 = (edge + 1) % len(edge_colors)
            pat.add_color_stop_rgba(0.0, *edge_colors[edge])
            pat.add_color_stop_rgba(1.0, *edge_colors[edge1])
            cr.set_source(pat)

            # Draw corners and edges with enough overlap to avoid
            # artefacts at touching line boundaries.
            cr.move_to(p0x, p0y)
            cr.line_to(p1x, p1y)
            cr.curve_to(p2[2], p2[3], p2[4], p2[5], p2[0], p2[1])
            cr.line_to(p3x, p3y)
            cr.line_to(ptop2x, ptop2y)
            if concave:
                cr.line_to(ptopbx, ptopby)
            cr.line_to(ptop1x, ptop1y)
            cr.line_to(ptop0x, ptop0y)
            cr.close_path()
            cr.fill()

            edge += 1
Beispiel #16
0
    def _draw_dish_key_border(self, cr, path, path_top,
                              polygon, polygon_top,
                              base_rgba, stroke_gradient, lightx, lighty):
        n = len(polygon)
        m = len(path)

        # Lambert lighting
        edge_colors = []
        for i in range(0, n, 2):
            x0 = polygon[i]
            y0 = polygon[i+1]
            if i < n-2:
                x1 = polygon[i+2]
                y1 = polygon[i+3]
            else:
                x1 = polygon[0]
                y1 = polygon[1]

            nx = y1 - y0
            ny = -(x1 - x0)
            ln = sqrt(nx*nx + ny*ny)
            I = (nx * lightx + ny * lighty) / ln \
                * stroke_gradient * 0.8 \
                if ln else 0.0
            edge_colors.append(brighten(I, *base_rgba))

        # draw border sections
        edge = 0
        for i in range(0, m-2, 2):
            # get path points
            i1 = i + 1
            i2 = i + 2
            if i2 >= m:
                i2 -= m
            i3 = i + 3
            if i3 >= m:
                i3 = 1

            p0 = path[i]
            p0x = p0[0]
            p0y = p0[1]

            p1 = path[i1]
            p1x = p1[0]
            p1y = p1[1]

            p2 = path[i2]
            p2x = p2[0]
            p2y = p2[1]

            p3 = path[i3]
            p3x = p3[0]
            p3y = p3[1]

            p = path_top[i]
            ptop0x = p[0]
            ptop0y = p[1]

            p = path_top[i1]
            ptop1x = p[0]
            ptop1y = p[1]

            p = path_top[i2]
            ptop2x = p[0]
            ptop2y = p[1]

            # get polygon points, only to
            # fill in gaps at concave corners.
            j0 = edge*2
            j1 = j0 + 2
            if j1 >= n:
                j1 -= n
            j2 = j0 + 4
            if j2 >= n:
                j2 -= n

            ptopax = polygon_top[j0]
            ptopay = polygon_top[j0 + 1]
            ptopbx = polygon_top[j1]
            ptopby = polygon_top[j1 + 1]
            ptopcx = polygon_top[j2]
            ptopcy = polygon_top[j2 + 1]
            vax = ptopbx - ptopax
            vay = ptopby - ptopay
            nbx = ptopcy - ptopby
            nby = -(ptopcx - ptopbx)
            concave = vax*nbx + vay*nby < 0.0

            # Fake Gouraud shading: draw a gradient between mid points
            # of the lines connecting the base with the top path.
            pat = cairo.LinearGradient((p1x + ptop1x) * 0.5,
                                        (p1y + ptop1y) * 0.5,
                                        (p2x + ptop2x) * 0.5,
                                        (p2y + ptop2y) * 0.5)
            edge1 = (edge + 1) % len(edge_colors)
            pat.add_color_stop_rgba(0.0, *edge_colors[edge])
            pat.add_color_stop_rgba(1.0, *edge_colors[edge1])
            cr.set_source (pat)

            # Draw corners and edges with enough overlap to avoid
            # artefacts at touching line boundaries.
            cr.move_to(p0x, p0y)
            cr.line_to(p1x, p1y)
            cr.curve_to(p2[2], p2[3], p2[4], p2[5], p2[0], p2[1])
            cr.line_to(p3x, p3y)
            cr.line_to(ptop2x, ptop2y)
            if concave:
                cr.line_to(ptopbx, ptopby)
            cr.line_to(ptop1x, ptop1y)
            cr.line_to(ptop0x, ptop0y)
            cr.close_path()
            cr.fill()

            edge += 1
Beispiel #17
0
    def draw_dish_key(self, cr, fill, line_width, lod):
        canvas_rect = self.get_canvas_rect()
        if self.geometry:
            geometry = self.geometry
        else:
            geometry = KeyGeometry.from_rect(self.get_border_rect())
        size_scale_x, size_scale_y = geometry.scale_log_to_size((1.0, 1.0))

        # compensate for smaller size due to missing stroke
        canvas_rect = canvas_rect.inflate(1.0)

        # parameters for the base path
        base_rgba = brighten(-0.200, *fill)
        stroke_gradient = self.get_stroke_gradient()
        light_dir = self.get_light_direction() - pi * 0.5  # 0 = light from top
        lightx = cos(light_dir)
        lighty = sin(light_dir)

        key_offset_x, key_offset_y, key_size_x, key_size_y = \
                                            self.get_key_offset_size(geometry)
        radius_pct = max(config.theme_settings.roundrect_radius, 2)
        radius_pct = max(radius_pct, 2) # too much +-1 fudging for square corners
        chamfer_size = self.get_chamfer_size()
        chamfer_size = (self.context.scale_log_to_canvas_x(chamfer_size) +
                        self.context.scale_log_to_canvas_y(chamfer_size)) * 0.5

        # parameters for the top path, key face
        stroke_width  = self.get_stroke_width()
        key_offset_top_y = key_offset_y - \
            config.DISH_KEY_Y_OFFSET * stroke_width
        border = config.DISH_KEY_BORDER
        scale_top_x = 1.0 - (border[0] * stroke_width * size_scale_x * 2.0)
        scale_top_y = 1.0 - (border[1] * stroke_width * size_scale_y * 2.0)
        key_size_top_x = key_size_x * scale_top_x
        key_size_top_y = key_size_y * scale_top_y
        chamfer_size_top = chamfer_size * (scale_top_x + scale_top_y) * 0.5

        # realize all paths we're going to use
        polygons, polygon_paths = \
            self.get_canvas_polygons(geometry,
                                   key_offset_x, key_offset_y,
                                   key_size_x, key_size_y,
                                   radius_pct, chamfer_size)
        polygons_top, polygon_paths_top = \
            self.get_canvas_polygons(geometry,
                                   key_offset_x, key_offset_top_y,
                                   key_size_top_x - size_scale_x,
                                   key_size_top_y - size_scale_y,
                                   radius_pct, chamfer_size_top)
        polygons_top1, polygon_paths_top1 = \
            self.get_canvas_polygons(geometry,
                                   key_offset_x, key_offset_top_y,
                                   key_size_top_x, key_size_top_y,
                                   radius_pct, chamfer_size_top)

        # draw key border
        if self.show_border:
            if not lod:
                cr.set_source_rgba(*base_rgba)
                for path in polygon_paths:
                    rounded_polygon_path_to_cairo_path(cr, path)
                    cr.fill()
            else:
                for ipg, polygon in enumerate(polygons):
                    polygon_top = polygons_top[ipg]
                    path = polygon_paths[ipg]
                    path_top = polygon_paths_top[ipg]

                    self._draw_dish_key_border(cr, path, path_top,
                                               polygon, polygon_top,
                                               base_rgba, stroke_gradient,
                                               lightx, lighty)

        # Draw the key face, the smaller top rectangle.
        if self.show_face:
            if not lod:
                cr.set_source_rgba(*fill)
            else:
                # Simulate the concave key dish with a gradient that has
                # a sligthly brighter middle section.
                if self.id == "SPCE":
                    angle = pi / 2.0  # space has a convex top
                else:
                    angle = 0.0       # all others are concave
                fill_gradient   = config.theme_settings.key_fill_gradient / 100.0
                dark_rgba = brighten(-fill_gradient*.5, *fill)
                bright_rgba = brighten(+fill_gradient*.5, *fill)
                gline = gradient_line(canvas_rect, angle)

                pat = cairo.LinearGradient (*gline)
                pat.add_color_stop_rgba(0.0, *dark_rgba)
                pat.add_color_stop_rgba(0.5, *bright_rgba)
                pat.add_color_stop_rgba(1.0, *dark_rgba)
                cr.set_source (pat)

            for path in polygon_paths_top1:
                rounded_polygon_path_to_cairo_path(cr, path)
                cr.fill()
Beispiel #18
0
    def draw_dish_key(self, context, rect, fill, line_width):
        # parameters for the base rectangle
        w, h = rect.get_size()
        w2, h2 = w * 0.5, h * 0.5
        xc, yc = rect.get_center()
        radius_pct = config.theme_settings.roundrect_radius
        radius_pct = max(radius_pct, 2) # too much +-1 fudging for square corners
        r, k = self.get_curved_rect_params(rect, radius_pct)

        base_rgba = brighten(-0.200, *fill)
        stroke_gradient = config.theme_settings.key_stroke_gradient / 100.0
        light_dir = config.theme_settings.key_gradient_direction / 180.0 * pi

        # lambert lighting
        edge_colors = []
        for edge in range(4):
            normal_dir = edge * pi / 2.0   # 0 = light from top
            I = cos(normal_dir - light_dir) * stroke_gradient * 0.8
            edge_colors.append(brighten(I, *base_rgba))

        # parameters for the top rectangle, key face
        border = self.context.scale_log_to_canvas(config.DISH_KEY_BORDER)
        offset_top = self.context.scale_log_to_canvas_y(config.DISH_KEY_Y_OFFSET)
        rect_top = rect.deflate(*border).offset(0, -offset_top)
        rect_top.w = max(rect_top.w, 0.0)
        rect_top.h = max(rect_top.h, 0.0)
        top_radius_scale = rect_top.h / float(rect.h)
        r_top, k_top = self.get_curved_rect_params(rect_top,
                                                radius_pct * top_radius_scale)

        context.save()
        context.translate(xc , yc)

        # edge sections, edge 0 = top
        for edge in range(4):
            if edge & 1:
                p = (h2, w2)
                p_top = [rect_top.h/2.0, rect_top.w/2.0]
            else:
                p = (w2, h2)
                p_top = [rect_top.w/2.0, rect_top.h/2.0]

            m = cairo.Matrix()
            m.rotate(edge * pi / 2.0)
            p0     = m.transform_point(-p[0] + r - 1, -p[1]) # -1 to fill gaps
            p1     = m.transform_point( p[0] - r + 1, -p[1])
            p0_top = m.transform_point( p_top[0] - r_top + 1, -p_top[1] + 1)
            p1_top = m.transform_point(-p_top[0] + r_top - 1, -p_top[1] + 1)
            p0_top = (p0_top[0], p0_top[1] - offset_top)
            p1_top = (p1_top[0], p1_top[1] - offset_top)

            context.set_source_rgba(*edge_colors[edge])
            context.move_to(p0[0], p0[1])
            context.line_to(p1[0], p1[1])
            context.line_to(*p0_top)
            context.line_to(*p1_top)
            context.close_path()
            context.fill()


        # corner sections
        for edge in range(4):
            if edge & 1:
                p = (h2, w2)
                p_top = [rect_top.h/2.0, rect_top.w/2.0]
            else:
                p = (w2, h2)
                p_top = [rect_top.w/2.0, rect_top.h/2.0]

            m = cairo.Matrix()
            m.rotate(edge * pi / 2.0)
            p1     = m.transform_point( p[0] - r, -p[1])
            p2     = m.transform_point( p[0],     -p[1] + r)
            pk0    = m.transform_point( p[0] - k, -p[1])
            pk1    = m.transform_point( p[0],     -p[1] + k)
            p0_top = m.transform_point( p_top[0] - r_top, -p_top[1])
            p2_top = m.transform_point( p_top[0],         -p_top[1] + r_top)
            p0_top = (p0_top[0], p0_top[1] - offset_top)
            p2_top = (p2_top[0], p2_top[1] - offset_top)

            # Fake Gouraud shading: draw a gradient between mid points
            # of the lines connecting the base with the top rectangle.
            gline = ((p1[0] + p0_top[0]) / 2.0, (p1[1] + p0_top[1]) / 2.0,
                     (p2[0] + p2_top[0]) / 2.0, (p2[1] + p2_top[1]) / 2.0)
            pat = cairo.LinearGradient (*gline)
            pat.add_color_stop_rgba(0.0, *edge_colors[edge])
            pat.add_color_stop_rgba(1.0, *edge_colors[(edge + 1) % 4])
            context.set_source (pat)

            context.move_to(*p1)
            context.curve_to(pk0[0], pk0[1], pk1[0], pk1[1], p2[0], p2[1])
            context.line_to(*p2_top)
            context.line_to(*p0_top)
            context.close_path()
            context.fill()

        context.restore()

        # Draw the key face, the smaller top rectangle.
        # Simulate the concave key dish with a gradient that has
        # a sligthly brighter middle section.
        if self.id == "SPCE":
            angle = pi / 2.0  # space has a convex top
        else:
            angle = 0.0       # all others are concave
        fill_gradient   = config.theme_settings.key_fill_gradient / 100.0
        dark_rgba = brighten(-fill_gradient*.5, *fill)
        bright_rgba = brighten(+fill_gradient*.5, *fill)
        gline = self.get_gradient_line(rect, angle)

        pat = cairo.LinearGradient (*gline)
        pat.add_color_stop_rgba(0.0, *dark_rgba)
        pat.add_color_stop_rgba(0.5, *bright_rgba)
        pat.add_color_stop_rgba(1.0, *dark_rgba)
        context.set_source (pat)

        self.build_rect_path(context, rect_top, top_radius_scale)
        context.fill()
Beispiel #19
0
    def get_key_default_rgba(self, key, element, state):
        colors = {
                    "fill":                     [0.9,  0.85, 0.7, 1.0],
                    "prelight":                 [0.0,  0.0,  0.0, 1.0],
                    "pressed":                  [0.6,  0.6,  0.6, 1.0],
                    "active":                   [0.5,  0.5,  0.5, 1.0],
                    "locked":                   [1.0,  0.0,  0.0, 1.0],
                    "scanned":                  [0.45, 0.45, 0.7, 1.0],
                    "stroke":                   [0.0,  0.0,  0.0, 1.0],
                    "label":                    [0.0,  0.0,  0.0, 1.0],
                    "secondary-label":          [0.5,  0.5,  0.5, 1.0],
                    "dwell-progress":           [0.82, 0.19, 0.25, 1.0],
                    "correction-label":         [1.0,  0.5,  0.5, 1.0],
                    }

        rgba = [0.0, 0.0, 0.0, 1.0]

        if element == "fill":
            if key.is_layer_button() and \
               not any(state.values()):
                # Special case for base fill color of layer buttons:
                # default color is layer fill color (as in onboard <=0.95).
                layer_index = key.get_layer_index()
                rgba = self.get_layer_fill_rgba(layer_index)

            elif state.get("pressed"):
                new_state = dict(list(state.items()))
                new_state["pressed"] = False
                rgba = self.get_key_rgba(key, element, new_state)

                # Make the default pressed color a slightly darker
                # or brighter variation of the unpressed color.
                h, l, s = colorsys.rgb_to_hls(*rgba[:3])

                # boost lightness changes for very dark and very bright colors
                # Ad-hoc formula, purly for aesthetics
                amount = -(log((l+.001)*(1-(l-.001))))*0.05 + 0.08

                if l < .5:  # dark color?
                    rgba = brighten(+amount, *rgba) # brigther
                else:
                    rgba = brighten(-amount, *rgba) # darker

            elif state.get("scanned"):
                rgba = colors["scanned"]
                # Make scanned active modifier keys stick out by blending
                # scanned color with non-scanned color.
                if state.get("active"): # includes locked
                    # inactive scanned color
                    new_state = dict(list(state.items()))
                    new_state["active"] = False
                    new_state["locked"] = False
                    scanned = self.get_key_rgba(key, element, new_state)

                    # unscanned fill color
                    new_state = dict(list(state.items()))
                    new_state["scanned"] = False
                    fill = self.get_key_rgba(key, element, new_state)

                    # blend inactive scanned color with unscanned fill color
                    for i in range(4):
                        rgba[i] = (scanned[i] + fill[i]) / 2.0

            elif state.get("prelight"):
                rgba = colors["prelight"]
            elif state.get("locked"):
                rgba = colors["locked"]
            elif state.get("active"):
                rgba = colors["active"]
            else:
                rgba = colors["fill"]

        elif element == "stroke":
            rgba == colors["stroke"]

        elif element == "label":

            if key.is_correction_key():
                rgba = colors["correction-label"]
            else:
                rgba = colors["label"]

            # dim label color for insensitive keys
            if state.get("insensitive"):
                rgba = self._get_insensitive_color(key, state, element)

        elif element == "secondary-label":

            rgba = colors["secondary-label"]

            # dim label color for insensitive keys
            if state.get("insensitive"):
                rgba = self._get_insensitive_color(key, state, element)

        elif element == "dwell-progress":
            rgba = colors["dwell-progress"]

        else:
            assert(False)   # unknown element

        return rgba
Beispiel #20
0
    def draw_dish_key(self, context, rect, fill, line_width, lod):
        # compensate for smaller size due to missing stroke
        rect = rect.inflate(1.0)

        # parameters for the base rectangle
        w, h = rect.get_size()
        w2, h2 = w * 0.5, h * 0.5
        xc, yc = rect.get_center()
        radius_pct = config.theme_settings.roundrect_radius
        radius_pct = max(radius_pct,
                         2)  # too much +-1 fudging for square corners
        r, k = self.get_curved_rect_params(rect, radius_pct)

        base_rgba = brighten(-0.200, *fill)
        stroke_gradient = config.theme_settings.key_stroke_gradient / 100.0
        light_dir = self.get_light_direction()

        # parameters for the top rectangle, key face
        scale = config.theme_settings.key_stroke_width / 100.0
        border = config.DISH_KEY_BORDER
        border = (border[0] * scale, border[1] * scale)

        border = self.context.scale_log_to_canvas(border)
        offset_top = self.context.scale_log_to_canvas_y(
            config.DISH_KEY_Y_OFFSET)
        rect_top = rect.deflate(*border).offset(0, -offset_top)
        rect_top.w = max(rect_top.w, 0.0)
        rect_top.h = max(rect_top.h, 0.0)
        top_radius_scale = rect_top.h / float(rect.h)
        r_top, k_top = self.get_curved_rect_params(
            rect_top, radius_pct * top_radius_scale)

        # draw key border
        if self.show_border:
            if not lod:
                self.build_rect_path(context, rect)
                context.set_source_rgba(*base_rgba)
                context.fill()
            else:

                # lambert lighting
                edge_colors = []
                for edge in range(4):
                    normal_dir = edge * pi / 2.0  # 0 = light from top
                    I = cos(normal_dir - light_dir) * stroke_gradient * 0.8
                    edge_colors.append(brighten(I, *base_rgba))

                context.save()
                context.translate(xc, yc)

                # edge sections, edge 0 = top
                for edge in range(4):
                    if edge & 1:
                        p = (h2, w2)
                        p_top = [rect_top.h / 2.0, rect_top.w / 2.0]
                    else:
                        p = (w2, h2)
                        p_top = [rect_top.w / 2.0, rect_top.h / 2.0]

                    m = cairo.Matrix()
                    m.rotate(edge * pi / 2.0)
                    p0 = m.transform_point(-p[0] + r - 1,
                                           -p[1])  # -1 to fill gaps
                    p1 = m.transform_point(p[0] - r + 1, -p[1])
                    p0_top = m.transform_point(p_top[0] - r_top + 1,
                                               -p_top[1] + 1)
                    p1_top = m.transform_point(-p_top[0] + r_top - 1,
                                               -p_top[1] + 1)
                    p0_top = (p0_top[0], p0_top[1] - offset_top)
                    p1_top = (p1_top[0], p1_top[1] - offset_top)

                    context.set_source_rgba(*edge_colors[edge])
                    context.move_to(p0[0], p0[1])
                    context.line_to(p1[0], p1[1])
                    context.line_to(*p0_top)
                    context.line_to(*p1_top)
                    context.close_path()
                    context.fill()

                # corner sections
                for edge in range(4):
                    if edge & 1:
                        p = (h2, w2)
                        p_top = [rect_top.h / 2.0, rect_top.w / 2.0]
                    else:
                        p = (w2, h2)
                        p_top = [rect_top.w / 2.0, rect_top.h / 2.0]

                    m = cairo.Matrix()
                    m.rotate(edge * pi / 2.0)
                    p1 = m.transform_point(p[0] - r, -p[1])
                    p2 = m.transform_point(p[0], -p[1] + r)
                    pk0 = m.transform_point(p[0] - k, -p[1])
                    pk1 = m.transform_point(p[0], -p[1] + k)
                    p0_top = m.transform_point(p_top[0] - r_top, -p_top[1])
                    p2_top = m.transform_point(p_top[0], -p_top[1] + r_top)
                    p0_top = (p0_top[0], p0_top[1] - offset_top)
                    p2_top = (p2_top[0], p2_top[1] - offset_top)

                    # Fake Gouraud shading: draw a gradient between mid points
                    # of the lines connecting the base with the top rectangle.
                    gline = ((p1[0] + p0_top[0]) / 2.0,
                             (p1[1] + p0_top[1]) / 2.0,
                             (p2[0] + p2_top[0]) / 2.0,
                             (p2[1] + p2_top[1]) / 2.0)
                    pat = cairo.LinearGradient(*gline)
                    pat.add_color_stop_rgba(0.0, *edge_colors[edge])
                    pat.add_color_stop_rgba(1.0, *edge_colors[(edge + 1) % 4])
                    context.set_source(pat)

                    context.move_to(*p1)
                    context.curve_to(pk0[0], pk0[1], pk1[0], pk1[1], p2[0],
                                     p2[1])
                    context.line_to(*p2_top)
                    context.line_to(*p0_top)
                    context.close_path()
                    context.fill()

                context.restore()

        # Draw the key face, the smaller top rectangle.
        if self.show_face:
            if not lod:
                context.set_source_rgba(*fill)
            else:
                # Simulate the concave key dish with a gradient that has
                # a sligthly brighter middle section.
                if self.id == "SPCE":
                    angle = pi / 2.0  # space has a convex top
                else:
                    angle = 0.0  # all others are concave
                fill_gradient = config.theme_settings.key_fill_gradient / 100.0
                dark_rgba = brighten(-fill_gradient * .5, *fill)
                bright_rgba = brighten(+fill_gradient * .5, *fill)
                gline = gradient_line(rect, angle)

                pat = cairo.LinearGradient(*gline)
                pat.add_color_stop_rgba(0.0, *dark_rgba)
                pat.add_color_stop_rgba(0.5, *bright_rgba)
                pat.add_color_stop_rgba(1.0, *dark_rgba)
                context.set_source(pat)

            self.build_rect_path(context, rect_top, top_radius_scale)
            context.fill()
Beispiel #21
0
    def get_key_default_rgba(self, key, element, state):
        colors = {
                    "fill":                   [0.9,  0.85, 0.7, 1.0],
                    "prelight":               [0.0,  0.0,  0.0, 1.0],
                    "pressed":                [0.6,  0.6,  0.6, 1.0],
                    "active":                 [0.5,  0.5,  0.5, 1.0],
                    "locked":                 [1.0,  0.0,  0.0, 1.0],
                    "scanned":                [0.45, 0.45, 0.7, 1.0],
                    "stroke":                 [0.0,  0.0,  0.0, 1.0],
                    "label":                  [0.0,  0.0,  0.0, 1.0],
                    "dwell-progress":         [0.82, 0.19, 0.25, 1.0],
                    }

        rgba = [0.0, 0.0, 0.0, 1.0]

        if element == "fill":
            if key.is_layer_button() and \
               not any(state.values()):
                # Special case for base fill color of layer buttons:
                # default color is layer fill color (as in onboard <=0.95).
                layer_index = key.get_layer_index()
                rgba = self.get_layer_fill_rgba(layer_index)
                
            elif state.get("pressed"):
                new_state = dict(list(state.items()))
                new_state["pressed"] = False
                rgba = self.get_key_rgba(key, element, new_state)

                # Make the default pressed color a slightly darker 
                # or brighter variation of the unpressed color.
                h, l, s = colorsys.rgb_to_hls(*rgba[:3])

                # boost lightness changes for very dark and very bright colors
                # Ad-hoc formula, purly for aesthetics
                amount = -(log((l+.001)*(1-(l-.001))))*0.05 + 0.08

                if l < .5:  # dark color?
                    rgba = brighten(+amount, *rgba) # brigther
                else:
                    rgba = brighten(-amount, *rgba) # darker

            elif state.get("scanned"):
                rgba = colors["scanned"]
            elif state.get("prelight"):
                rgba = colors["prelight"]
            elif state.get("locked"):
                rgba = colors["locked"]
            elif state.get("active"):
                rgba = colors["active"]
            else:
                rgba = colors["fill"]

        elif element == "stroke":
            rgba == colors["stroke"]

        elif element == "label":
            rgba = colors["label"]

            # dim label color for insensitive keys
            if state.get("insensitive"):
                new_state = dict(list(state.items()))
                new_state["insensitive"] = False
                fill = self.get_key_rgba(key, "fill", new_state)
                rgba = self.get_key_rgba(key, "label", new_state)

                h, lf, s = colorsys.rgb_to_hls(*fill[:3])
                h, ll, s = colorsys.rgb_to_hls(*rgba[:3])

                # Leave only one third of the lightness difference
                # between label and fill color.
                amount = (ll - lf) * 2.0 / 3.0
                rgba = brighten(-amount, *rgba)

        elif element == "dwell-progress":
            rgba = colors["dwell-progress"]

        else:
            assert(False)   # unknown element

        return rgba
Beispiel #22
0
    def get_key_default_rgba(self, key, element, state):
        colors = {
            "fill": [0.9, 0.85, 0.7, 1.0],
            "prelight": [0.0, 0.0, 0.0, 1.0],
            "pressed": [0.6, 0.6, 0.6, 1.0],
            "active": [0.5, 0.5, 0.5, 1.0],
            "locked": [1.0, 0.0, 0.0, 1.0],
            "scanned": [0.45, 0.45, 0.7, 1.0],
            "stroke": [0.0, 0.0, 0.0, 1.0],
            "label": [0.0, 0.0, 0.0, 1.0],
            "secondary-label": [0.5, 0.5, 0.5, 1.0],
            "dwell-progress": [0.82, 0.19, 0.25, 1.0],
            "correction-label": [1.0, 0.5, 0.5, 1.0],
        }

        rgba = [0.0, 0.0, 0.0, 1.0]

        if element == "fill":
            if key.is_layer_button() and \
               not any(state.values()):
                # Special case for base fill color of layer buttons:
                # default color is layer fill color (as in onboard <=0.95).
                layer_index = key.get_layer_index()
                rgba = self.get_layer_fill_rgba(layer_index)

            elif state.get("pressed"):
                new_state = dict(list(state.items()))
                new_state["pressed"] = False
                rgba = self.get_key_rgba(key, element, new_state)

                # Make the default pressed color a slightly darker
                # or brighter variation of the unpressed color.
                h, l, s = colorsys.rgb_to_hls(*rgba[:3])

                # boost lightness changes for very dark and very bright colors
                # Ad-hoc formula, purly for aesthetics
                amount = -(log((l + .001) * (1 - (l - .001)))) * 0.05 + 0.08

                if l < .5:  # dark color?
                    rgba = brighten(+amount, *rgba)  # brigther
                else:
                    rgba = brighten(-amount, *rgba)  # darker

            elif state.get("scanned"):
                rgba = colors["scanned"]
                # Make scanned active modifier keys stick out by blending
                # scanned color with non-scanned color.
                if state.get("active"):  # includes locked
                    # inactive scanned color
                    new_state = dict(list(state.items()))
                    new_state["active"] = False
                    new_state["locked"] = False
                    scanned = self.get_key_rgba(key, element, new_state)

                    # unscanned fill color
                    new_state = dict(list(state.items()))
                    new_state["scanned"] = False
                    fill = self.get_key_rgba(key, element, new_state)

                    # blend inactive scanned color with unscanned fill color
                    for i in range(4):
                        rgba[i] = (scanned[i] + fill[i]) / 2.0

            elif state.get("prelight"):
                rgba = colors["prelight"]
            elif state.get("locked"):
                rgba = colors["locked"]
            elif state.get("active"):
                rgba = colors["active"]
            else:
                rgba = colors["fill"]

        elif element == "stroke":
            rgba == colors["stroke"]

        elif element == "label":

            if key.is_correction_key():
                rgba = colors["correction-label"]
            else:
                rgba = colors["label"]

            # dim label color for insensitive keys
            if state.get("insensitive"):
                rgba = self._get_insensitive_color(key, state, element)

        elif element == "secondary-label":

            rgba = colors["secondary-label"]

            # dim label color for insensitive keys
            if state.get("insensitive"):
                rgba = self._get_insensitive_color(key, state, element)

        elif element == "dwell-progress":
            rgba = colors["dwell-progress"]

        else:
            assert (False)  # unknown element

        return rgba
Beispiel #23
0
    def draw_dish_key(self, cr, fill, line_width, lod):
        canvas_rect = self.get_canvas_rect()
        if self.geometry:
            geometry = self.geometry
        else:
            geometry = KeyGeometry.from_rect(self.get_border_rect())
        size_scale_x, size_scale_y = geometry.scale_log_to_size((1.0, 1.0))

        # compensate for smaller size due to missing stroke
        canvas_rect = canvas_rect.inflate(1.0)

        # parameters for the base path
        base_rgba = brighten(-0.200, *fill)
        stroke_gradient = self.get_stroke_gradient()
        light_dir = self.get_light_direction() - pi * 0.5  # 0 = light from top
        lightx = cos(light_dir)
        lighty = sin(light_dir)

        key_offset_x, key_offset_y, key_size_x, key_size_y = \
                                            self.get_key_offset_size(geometry)
        radius_pct = max(config.theme_settings.roundrect_radius, 2)
        radius_pct = max(radius_pct,
                         2)  # too much +-1 fudging for square corners
        chamfer_size = self.get_chamfer_size()
        chamfer_size = (self.context.scale_log_to_canvas_x(chamfer_size) +
                        self.context.scale_log_to_canvas_y(chamfer_size)) * 0.5

        # parameters for the top path, key face
        stroke_width = self.get_stroke_width()
        key_offset_top_y = key_offset_y - \
            config.DISH_KEY_Y_OFFSET * stroke_width
        border = config.DISH_KEY_BORDER
        scale_top_x = 1.0 - (border[0] * stroke_width * size_scale_x * 2.0)
        scale_top_y = 1.0 - (border[1] * stroke_width * size_scale_y * 2.0)
        key_size_top_x = key_size_x * scale_top_x
        key_size_top_y = key_size_y * scale_top_y
        chamfer_size_top = chamfer_size * (scale_top_x + scale_top_y) * 0.5

        # realize all paths we're going to use
        polygons, polygon_paths = \
            self.get_canvas_polygons(geometry,
                                   key_offset_x, key_offset_y,
                                   key_size_x, key_size_y,
                                   radius_pct, chamfer_size)
        polygons_top, polygon_paths_top = \
            self.get_canvas_polygons(geometry,
                                   key_offset_x, key_offset_top_y,
                                   key_size_top_x - size_scale_x,
                                   key_size_top_y - size_scale_y,
                                   radius_pct, chamfer_size_top)
        polygons_top1, polygon_paths_top1 = \
            self.get_canvas_polygons(geometry,
                                   key_offset_x, key_offset_top_y,
                                   key_size_top_x, key_size_top_y,
                                   radius_pct, chamfer_size_top)

        # draw key border
        if self.show_border:
            if not lod:
                cr.set_source_rgba(*base_rgba)
                for path in polygon_paths:
                    rounded_polygon_path_to_cairo_path(cr, path)
                    cr.fill()
            else:
                for ipg, polygon in enumerate(polygons):
                    polygon_top = polygons_top[ipg]
                    path = polygon_paths[ipg]
                    path_top = polygon_paths_top[ipg]

                    self._draw_dish_key_border(cr, path, path_top, polygon,
                                               polygon_top, base_rgba,
                                               stroke_gradient, lightx, lighty)

        # Draw the key face, the smaller top rectangle.
        if self.show_face:
            if not lod:
                cr.set_source_rgba(*fill)
            else:
                # Simulate the concave key dish with a gradient that has
                # a sligthly brighter middle section.
                if self.id == "SPCE":
                    angle = pi / 2.0  # space has a convex top
                else:
                    angle = 0.0  # all others are concave
                fill_gradient = config.theme_settings.key_fill_gradient / 100.0
                dark_rgba = brighten(-fill_gradient * .5, *fill)
                bright_rgba = brighten(+fill_gradient * .5, *fill)
                gline = gradient_line(canvas_rect, angle)

                pat = cairo.LinearGradient(*gline)
                pat.add_color_stop_rgba(0.0, *dark_rgba)
                pat.add_color_stop_rgba(0.5, *bright_rgba)
                pat.add_color_stop_rgba(1.0, *dark_rgba)
                cr.set_source(pat)

            for path in polygon_paths_top1:
                rounded_polygon_path_to_cairo_path(cr, path)
                cr.fill()