use nami::Computed;
use std::cell::RefCell;
use std::rc::Rc;
use waterui::component::badge::BadgeConfig;
use waterui_core::layout::{HorizontalAlignment, ProposalSize, Size as LayoutSize, ViewDimensions};
use waterui_core::{Environment, Native};
use waterui_text::styled::StyledStr;
#[cfg(feature = "accessibility")]
use crate::renderer::transformed_rect;
use crate::renderer::{
HydroNativeView, HydroState, HydrolysisRenderer, RenderContext, RetainedSubview,
WidgetRenderContext, measure_view_dimensions_with_proposal, measure_view_intrinsic,
normalize_view_for_render,
};
use crate::widgets::widget_theme;
#[cfg(feature = "accessibility")]
use accesskit::{Node as AccessibilityNode, Role as AccessibilityNodeRole};
pub(crate) struct BadgeRenderState {
value: Computed<i32>,
content: RetainedSubview,
}
impl BadgeRenderState {
pub(crate) fn from_config(config: BadgeConfig) -> Self {
let BadgeConfig { value, content, .. } = config;
Self {
value,
content: RetainedSubview::new(content.build()),
}
}
pub(crate) fn prebuild_content(
&mut self,
renderer: &mut HydrolysisRenderer,
env: &Environment,
) {
self.content.ensure_built(renderer, env);
}
}
fn badge_content_size(
state: &mut HydroState,
badge: &Native<BadgeConfig>,
env: &Environment,
) -> LayoutSize {
let content = normalize_view_for_render(badge.as_inner().content.build(), env);
measure_view_intrinsic(&content, state, env)
}
fn badge_large_label(value: i32, env: &Environment) -> StyledStr {
let theme = widget_theme(env);
StyledStr::plain(value.to_string())
.font(theme.badge_label_font())
.foreground(theme.badge_label_color())
}
impl HydroNativeView for Native<BadgeConfig> {
fn intrinsic(state: &mut HydroState, badge: &Self, env: &Environment) -> LayoutSize {
badge_content_size(state, badge, env)
}
fn dimensions(
state: &mut HydroState,
badge: &Self,
env: &Environment,
proposal: ProposalSize,
) -> ViewDimensions {
let content = normalize_view_for_render(badge.as_inner().content.build(), env);
measure_view_dimensions_with_proposal(&content, proposal, state, env)
}
}
pub(crate) fn measure_badge_node(
state: &BadgeRenderState,
_proposal: ProposalSize,
hydro: &mut HydroState,
env: &Environment,
) -> ViewDimensions {
ViewDimensions::new(state.content.measure_built(hydro, env))
}
pub(crate) fn render_badge_node(
ctx: &mut WidgetRenderContext<'_>,
state: &Rc<RefCell<BadgeRenderState>>,
env: &Environment,
) {
render_badge_parts(ctx, state, env);
}
pub(crate) fn render_badge_parts(
ctx: &mut WidgetRenderContext<'_>,
state: &Rc<RefCell<BadgeRenderState>>,
env: &Environment,
) {
let bounds = ctx.bounds;
{
let render_ctx = ctx.render_context();
let mut state = state.borrow_mut();
state
.content
.flush_in_rect(ctx.renderer_mut(), render_ctx, env, bounds);
}
let theme = widget_theme(env);
let metrics = theme.badge_metrics();
let value = {
let signal = state.borrow().value.clone();
ctx.renderer_mut().read_signal(&signal)
};
let content_width = ctx.bounds.width();
let x0 = if value == 0 {
ctx.bounds.x0 + content_width * 0.5 + metrics.small_offset_x
} else {
ctx.bounds.x0 + content_width * 0.5 + metrics.large_offset_x
};
let y0 = if value == 0 {
ctx.bounds.y0 + metrics.small_offset_y
} else {
ctx.bounds.y0 + metrics.large_offset_y
};
if value == 0 {
let rect =
vello::kurbo::Rect::new(x0, y0, x0 + metrics.small_size, y0 + metrics.small_size);
let mut draw = ctx.draw_context();
theme.draw_badge_small(&mut draw, rect);
return;
}
let label = badge_large_label(value, env);
let text_size = HydrolysisRenderer::measure_text_dimensions(
ctx.state_mut(),
label.clone(),
HorizontalAlignment::Center,
env,
None,
Some(1),
)
.size;
let width = (f64::from(text_size.width) + metrics.large_horizontal_padding * 2.0)
.max(metrics.large_size);
let rect = vello::kurbo::Rect::new(x0, y0, x0 + width, y0 + metrics.large_size);
{
let mut draw = ctx.draw_context();
theme.draw_badge_large(&mut draw, rect);
}
#[cfg(feature = "accessibility")]
{
let mut node = AccessibilityNode::new(AccessibilityNodeRole::Label);
node.set_label(value.to_string());
let node_bounds = transformed_rect(ctx.hit_transform, rect);
let _ = ctx
.renderer_mut()
.register_accessibility_node(node, node_bounds, env, None);
}
let text_height = f64::from(text_size.height);
let text_rect = vello::kurbo::Rect::new(
rect.x0,
rect.y0 + (rect.height() - text_height) * 0.5,
rect.x1,
rect.y1,
);
let text_ctx = RenderContext {
transform: ctx.transform,
hit_transform: ctx.hit_transform,
bounds: text_rect,
};
let (hydro, scene) = ctx.renderer_mut().state_and_scene_mut();
HydrolysisRenderer::render_styled_text(
hydro,
scene,
text_ctx,
label,
HorizontalAlignment::Center,
env,
);
}