#include "UiButtonWidget.hpp" #include #include #include "PaintUtil.hpp" #include "Theme.hpp" namespace jdi { namespace { const float kButtonRadius = 5.0f; /**< 按钮圆角半径(逻辑像素)。 */ /** @brief 按比例把一个颜色向目标色混合(与旧 Toolbar 的写法保持一致)。 */ std::uint32_t BlendToward(std::uint32_t base, std::uint32_t target, double ratio) { return MixColor(base, target, ratio); } } void UiButtonWidget::Configure(const UiFonts* fonts, const Theme* theme, float scale, std::wstring label, bool primary, bool toggle, float textPadding, float height) { m_fonts = fonts; m_theme = theme; m_scale = (scale > 0.1f) ? scale : 1.0f; m_label = std::move(label); m_primary = primary; m_toggle = toggle; m_textPadding = textPadding; m_height = height; InvalidateMeasure(); Invalidate(Bounds()); } void UiButtonWidget::SetLabel(std::wstring label) { if (m_label == label) { return; } m_label = std::move(label); InvalidateMeasure(); Invalidate(Bounds()); } void UiButtonWidget::SetChecked(bool checked) { if (m_checked == checked) { return; } m_checked = checked; Invalidate(Bounds()); } void UiButtonWidget::SetEnabled(bool enabled) { if (m_enabled == enabled) { return; } m_enabled = enabled; Invalidate(Bounds()); } void UiButtonWidget::CancelPress() { if (!m_pressed) { return; } m_pressed = false; ReleaseMouse(); Invalidate(Bounds()); } gd::SizeF UiButtonWidget::OnMeasure(const UiMeasureContext& ctx) { const float scale = (ctx.scale > 0.1f) ? ctx.scale : m_scale; const float width = (m_fonts != nullptr && m_fonts->ui != nullptr) ? (m_textPadding * 2.0f * scale + UiMeasureTextWidth(m_fonts->ui, m_label)) : 0.0f; return gd::SizeF(width, m_height * scale); } void UiButtonWidget::OnPaint(UiPaintContext& ctx) { if (m_theme == nullptr || m_fonts == nullptr || ctx.g == nullptr) { return; } const gd::RectF& rect = Bounds(); const bool hovered = Hovered(); const bool enabled = m_enabled; std::uint32_t background = 0; std::uint32_t border = 0; std::uint32_t textColor = 0; if (m_primary) { background = enabled ? m_theme->accent : BlendToward(m_theme->accent, m_canvasColor, 0.6); if (m_pressed) { background = BlendToward(m_theme->accent, 0xFF000000u, 0.12); } else if (enabled && hovered) { background = BlendToward(m_theme->accent, 0xFFFFFFFFu, 0.12); } border = background; textColor = m_theme->textOnAccent; } else { background = (m_toggle && m_checked) ? m_theme->accentSoft : m_canvasColor; if (m_pressed) { background = m_theme->pressed; } else if (enabled && hovered) { background = (m_toggle && m_checked) ? BlendToward(m_theme->accentSoft, m_theme->accent, 0.16) : m_theme->hover; } if (!enabled) { background = m_canvasColor; } if (m_toggle && m_checked && enabled) { border = m_theme->accent; } else { border = BlendToward(m_theme->border, m_canvasColor, enabled ? 0.0 : 0.5); } textColor = enabled ? ((m_toggle && m_checked) ? m_theme->textStrong : m_theme->text) : m_theme->textMuted; } if (background != m_canvasColor) { UiFillRoundRect(*ctx.g, rect, kButtonRadius * m_scale, background); } if (!m_primary) { UiDrawRoundRect(*ctx.g, rect, kButtonRadius * m_scale, border, 1.0f); } // ⚠️ 与旧实现一致:文字走 `UiText`(GDI,不受 GDI+ 裁剪约束)。 // 按钮矩形本身就在裁剪区内(外扩 1px 只影响描边),因此不需要 `UiTextClipped`。 UiText(*ctx.g, m_label, m_fonts->ui, rect, textColor, TextAlign::CENTER); } UiEventResult UiButtonWidget::OnEvent(UiEvent& event) { switch (event.kind) { case UiEventKind::MOUSE_DOWN: if (event.button != UiMouseButton::LEFT || !m_enabled) { return UiEventResult(); } m_pressed = true; CaptureMouse(); { UiEventResult result = UiEventResult::Handled(); result.dirty = Bounds(); return result; } case UiEventKind::MOUSE_UP: if (event.button != UiMouseButton::LEFT || !m_pressed) { return UiEventResult(); } // 只有"在自身范围内松开"才算点击(按住移开再松开 = 放弃)。 m_pressed = false; ReleaseMouse(); { UiEventResult result = UiEventResult::Handled(); result.dirty = Bounds(); if (Contains(event.x, event.y) && m_enabled && onClick) { onClick(); } return result; } case UiEventKind::MOUSE_LEAVE: if (m_pressed) { m_pressed = false; ReleaseMouse(); UiEventResult result = UiEventResult::Handled(); result.dirty = Bounds(); return result; } return UiEventResult(); default: return UiEventResult(); } } HCURSOR UiButtonWidget::OnQueryCursor(int x, int y) { (void)x; (void)y; // 与旧实现一致:工具栏按钮不改变光标形态(箭头)。 return nullptr; } std::uint64_t UiButtonWidget::ContentKey() const { std::uint64_t key = BaseContentKey(); key = UiMixKey(key, static_cast(m_label.size())); key = UiMixKey(key, m_primary ? 1u : 0u); key = UiMixKey(key, m_toggle ? 1u : 0u); key = UiMixKey(key, m_checked ? 1u : 0u); key = UiMixKey(key, m_enabled ? 1u : 0u); key = UiMixKey(key, m_pressed ? 1u : 0u); key = UiMixKey(key, Hovered() ? 1u : 0u); return key; } } // namespace jdi