use crate::render::Canvas;
use crate::types::{ Color, Rect };
use crate::Keysym;
use super::{ Action, Modifier, Panel };
const TEXT_SIZE: f32 = 20.0;
const ACTION_SIZE: f32 = 14.0;
const ICON_SIZE: f32 = 22.0;
const ARROW_SIZE: f32 = 14.0;
const RADIUS: f32 = 6.0;
fn icon_for( key: &super::Key ) -> Option<&'static str>
{
match ( &key.action, key.keysym )
{
( Some( Action::Shift ), _ ) => Some( "keyboard/shift" ),
( Some( Action::Backspace ), _ ) => Some( "keyboard/backspace" ),
( Some( Action::Enter ), _ ) => Some( "keyboard/enter" ),
( Some( Action::Hide ), _ ) => Some( "general/down-simple" ),
( Some( Action::Layout ), _ ) => Some( "accessibility/languages" ),
( _, Some( Keysym::Left ) ) => Some( "general/left" ),
( _, Some( Keysym::Right ) ) => Some( "general/right" ),
( _, Some( Keysym::Up ) ) => Some( "general/up" ),
( _, Some( Keysym::Down ) ) => Some( "general/down" ),
_ => None,
}
}
impl Panel
{
pub fn draw( &mut self, canvas: &mut Canvas )
{
let palette = crate::theme::palette();
let scale = self.scale;
canvas.fill_rect( self.rect, palette.surface_alt, 0.0 );
for i in 0..self.keys.len()
{
let ( rect, key ) = ( self.keys[ i ].rect, self.keys[ i ].key.clone() );
let action = key.action.is_some() || key.keysym.is_some() || key.modifier.is_some();
let pressed = self.pressed() == Some( i );
let held = match key.modifier
{
Some( Modifier::Ctrl ) => self.modifiers().ctrl,
Some( Modifier::Alt ) => self.modifiers().alt,
None => key.action == Some( Action::Shift ) && self.shift_locked(),
};
let lit = pressed || held;
let bg = if lit { palette.accent } else if action { palette.divider } else { palette.surface };
let ink = if lit { palette.bg } else { palette.text_primary };
canvas.fill_rect( rect, bg, RADIUS * scale );
let size = if key.keysym.is_some() { ARROW_SIZE } else { ICON_SIZE };
let icon = icon_for( &key ).and_then( |name| self.icon( name, ( size * scale ) as u32, ink ) );
match icon
{
Some( ( rgba, w, h ) ) =>
{
let dest = Rect { x: rect.x + ( rect.width - w as f32 ) / 2.0, y: rect.y + ( rect.height - h as f32 ) / 2.0, width: w as f32, height: h as f32 };
canvas.draw_image_data( &rgba, w, h, dest, 1.0 );
}
None =>
{
let size = if action { ACTION_SIZE } else { TEXT_SIZE };
draw_centred( canvas, &key.label, rect, size, ink );
}
}
}
if let Some( popup ) = &self.popup
{
canvas.fill_rect( popup.bounds, palette.surface, RADIUS * scale );
canvas.stroke_rect( popup.bounds, palette.divider, 1.0, RADIUS * scale );
for ( i, ( text, rect ) ) in popup.variants.iter().zip( &popup.rects ).enumerate()
{
let lit = popup.selected == Some( i );
let bg = if lit { palette.accent } else { palette.surface };
let ink = if lit { palette.bg } else { palette.text_primary };
canvas.fill_rect( *rect, bg, RADIUS * scale );
draw_centred( canvas, text, *rect, TEXT_SIZE, ink );
}
}
}
fn icon( &mut self, name: &'static str, size: u32, ink: Color ) -> Option<crate::ImageData>
{
let key = ( name, size, [ channel( ink.r ), channel( ink.g ), channel( ink.b ), channel( ink.a ) ] );
self.icons.entry( key )
.or_insert_with( || crate::theme_icon_tinted( name, size, ink ) )
.clone()
}
}
fn channel( v: f32 ) -> u8
{
( v.clamp( 0.0, 1.0 ) * 255.0 ).round() as u8
}
fn draw_centred( canvas: &mut Canvas, text: &str, rect: Rect, size: f32, color: Color )
{
if text.is_empty() { return; }
let w = canvas.measure_text( text, size );
let glyph_h = size * canvas.dpi_scale();
canvas.draw_text( text, rect.x + ( rect.width - w ) / 2.0, rect.y + rect.height / 2.0 + glyph_h * 0.35, size, color );
}