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)
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)
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()
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()
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)
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
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()
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()
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()
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)
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)
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)
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
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
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()
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()
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
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()
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
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
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()