fix text sizing, and add custom hotkey support
This commit is contained in:
parent
09590edd95
commit
ee425e224e
10 changed files with 1096 additions and 280 deletions
|
|
@ -10,7 +10,9 @@ use uuid::Uuid;
|
|||
use chattyness_db::models::ChannelMemberWithAvatar;
|
||||
|
||||
use super::chat_types::{emotion_bubble_colors, ActiveBubble};
|
||||
use super::settings::BASE_PROP_SIZE;
|
||||
|
||||
/// Base text size multiplier. Text at 100% slider = base_sizes * 1.4
|
||||
const BASE_TEXT_SCALE: f64 = 1.4;
|
||||
|
||||
/// Get a unique key for a member (for Leptos For keying).
|
||||
pub fn member_key(m: &ChannelMemberWithAvatar) -> (Option<Uuid>, Option<Uuid>) {
|
||||
|
|
@ -41,6 +43,9 @@ pub fn AvatarCanvas(
|
|||
z_index: i32,
|
||||
/// Active speech bubble for this user (if any).
|
||||
active_bubble: Option<ActiveBubble>,
|
||||
/// Text size multiplier for display names, chat bubbles, and badges.
|
||||
#[prop(default = 1.0)]
|
||||
text_em_size: f64,
|
||||
) -> impl IntoView {
|
||||
let canvas_ref = NodeRef::<leptos::html::Canvas>::new();
|
||||
|
||||
|
|
@ -54,11 +59,24 @@ pub fn AvatarCanvas(
|
|||
let canvas_x = member.member.position_x * scale_x + offset_x - prop_size / 2.0;
|
||||
let canvas_y = member.member.position_y * scale_y + offset_y - prop_size;
|
||||
|
||||
// Calculate canvas size (extra height for bubble and name)
|
||||
let bubble_extra = if active_bubble.is_some() { prop_size * 1.5 } else { 0.0 };
|
||||
let name_extra = 20.0;
|
||||
let canvas_width = prop_size.max(200.0); // Wide enough for bubble
|
||||
let canvas_height = prop_size + bubble_extra + name_extra;
|
||||
// Fixed text dimensions (independent of prop_size/zoom)
|
||||
// Text stays readable regardless of zoom level - only affected by text_em_size slider
|
||||
let text_scale = text_em_size * BASE_TEXT_SCALE;
|
||||
let fixed_bubble_height = if active_bubble.is_some() {
|
||||
// 4 lines * 16px line_height + 16px padding + 8px tail + 5px margin
|
||||
(4.0 * 16.0 + 16.0 + 8.0 + 5.0) * text_scale
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let fixed_name_height = 20.0 * text_scale;
|
||||
let fixed_text_width = 200.0 * text_scale;
|
||||
|
||||
// Canvas must fit both avatar AND fixed-size text
|
||||
let canvas_width = prop_size.max(fixed_text_width);
|
||||
let canvas_height = prop_size + fixed_bubble_height + fixed_name_height;
|
||||
|
||||
// Adjust bubble_extra for positioning (used later in avatar_cy calculation)
|
||||
let bubble_extra = fixed_bubble_height;
|
||||
|
||||
// Adjust position to account for extra space above avatar
|
||||
let adjusted_y = canvas_y - bubble_extra;
|
||||
|
|
@ -184,8 +202,8 @@ pub fn AvatarCanvas(
|
|||
draw_image(emotion_path, &image_cache, &ctx, avatar_cx, avatar_cy, prop_size);
|
||||
}
|
||||
|
||||
// Scale factor for text/badges
|
||||
let text_scale = prop_size / BASE_PROP_SIZE;
|
||||
// Text scale independent of zoom - only affected by user's text_em_size setting
|
||||
let text_scale = text_em_size * BASE_TEXT_SCALE;
|
||||
|
||||
// Draw emotion badge if non-neutral
|
||||
if current_emotion > 0 {
|
||||
|
|
@ -205,18 +223,24 @@ pub fn AvatarCanvas(
|
|||
let _ = ctx.fill_text(&format!("{}", current_emotion), badge_x, badge_y);
|
||||
}
|
||||
|
||||
// Draw display name below avatar
|
||||
ctx.set_fill_style_str("#fff");
|
||||
// Draw display name below avatar (with black outline for readability)
|
||||
ctx.set_font(&format!("{}px sans-serif", 12.0 * text_scale));
|
||||
ctx.set_text_align("center");
|
||||
ctx.set_text_baseline("alphabetic");
|
||||
let _ = ctx.fill_text(&display_name_clone, avatar_cx, avatar_cy + prop_size / 2.0 + 15.0 * text_scale);
|
||||
let name_y = avatar_cy + prop_size / 2.0 + 15.0 * text_scale;
|
||||
// Black outline
|
||||
ctx.set_stroke_style_str("#000");
|
||||
ctx.set_line_width(3.0);
|
||||
let _ = ctx.stroke_text(&display_name_clone, avatar_cx, name_y);
|
||||
// White fill
|
||||
ctx.set_fill_style_str("#fff");
|
||||
let _ = ctx.fill_text(&display_name_clone, avatar_cx, name_y);
|
||||
|
||||
// Draw speech bubble if active
|
||||
if let Some(ref bubble) = active_bubble_clone {
|
||||
let current_time = js_sys::Date::now() as i64;
|
||||
if bubble.expires_at >= current_time {
|
||||
draw_bubble(&ctx, bubble, avatar_cx, avatar_cy - prop_size / 2.0, prop_size);
|
||||
draw_bubble(&ctx, bubble, avatar_cx, avatar_cy - prop_size / 2.0, prop_size, text_em_size);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -248,9 +272,11 @@ fn draw_bubble(
|
|||
bubble: &ActiveBubble,
|
||||
center_x: f64,
|
||||
top_y: f64,
|
||||
prop_size: f64,
|
||||
_prop_size: f64,
|
||||
text_em_size: f64,
|
||||
) {
|
||||
let text_scale = prop_size / BASE_PROP_SIZE;
|
||||
// Text scale independent of zoom - only affected by user's text_em_size setting
|
||||
let text_scale = text_em_size * BASE_TEXT_SCALE;
|
||||
let max_bubble_width = 200.0 * text_scale;
|
||||
let padding = 8.0 * text_scale;
|
||||
let font_size = 12.0 * text_scale;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue