Files
msime-windows/ui/include/msimeui/Controls.h
T
fanlusky 5421c1dd1b Add SVG icon rendering, toggle switch, and font fallback for MenuFlyoutItem
MenuFlyoutItem gains leading SVG icons (ID2D1SvgDocument), an optional
trailing toggle switch, and content-based width measurement so tray-style
menu items match the HTML menu layout. Default UI font is now Noto Sans SC
with Microsoft YaHei fallback via IDWriteFontFallback; CreateTextFormat
gracefully falls back when the primary family is missing.
2026-09-02 16:03:57 +08:00

758 lines
25 KiB
C++

#pragma once
#include "Layout.h"
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <wrl/client.h>
class CTextEditor;
namespace msimeui
{
enum class PopupPlacement
{
BelowLeading,
AboveLeading,
};
enum class ImageStretch
{
None,
Fill,
Uniform,
UniformToFill,
};
class Image : public Visual
{
public:
explicit Image(std::wstring filePath);
void SetSource(std::wstring filePath);
const std::wstring &GetSource() const;
void SetStretch(ImageStretch stretch);
void SetOpacity(float opacity);
void SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE interpolationMode);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
private:
void LoadNaturalSize();
std::wstring filePath_;
SizeF naturalSize_ = {};
bool naturalSizeLoaded_ = false;
ImageStretch stretch_ = ImageStretch::Uniform;
float opacity_ = 1.0f;
D2D1_BITMAP_INTERPOLATION_MODE interpolationMode_ = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
};
class TextBox : public Visual
{
public:
using TextChangedHandler = std::function<void(const std::wstring &text)>;
TextBox(float height, std::wstring placeholder);
~TextBox() override;
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
void Attach(Window *window) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
bool OnMouseMove(const POINT &point, WPARAM keyState) override;
bool OnKeyDown(WPARAM key, LPARAM lParam) override;
bool OnChar(wchar_t ch, LPARAM lParam) override;
bool OnTimer(UINT_PTR timerId) override;
HCURSOR GetCursor() const override;
std::wstring GetText() const;
bool IsFocused() const { return focused_; }
void SetOnTextChanged(TextChangedHandler handler);
void SetOnFocusChanged(std::function<void(bool focused)> handler);
void SetFontSize(float fontSizeDips);
void SetPlaceholderFontSize(float fontSizeDips);
void SetPlaceholderText(std::wstring placeholder);
void SetChromeVisible(bool visible);
private:
RECT ComputeEditorHostRect() const;
RECT ComputeEditorContentPadding() const;
POINT ToLocalPoint(const POINT &point) const;
bool EnsureInitialized(DeviceResources *deviceResources);
bool AlertMouseSink(const POINT &point, WPARAM keyState);
float preferredHeight_ = 44.0f;
std::wstring placeholder_;
bool focused_ = false;
bool tsfInitialized_ = false;
bool renderInitialized_ = false;
UINT dragSelectionStart_ = static_cast<UINT>(-1);
::CTextEditor *editor_ = nullptr;
LOGFONT font_ = {};
TextChangedHandler onTextChanged_;
std::function<void(bool focused)> onFocusChanged_;
float fontSizeDips_ = 18.0f;
float placeholderFontSizeDips_ = 16.0f;
bool chromeVisible_ = true;
};
class Button : public Visual
{
public:
using ClickHandler = std::function<void()>;
Button(std::wstring text, float height = 44.0f);
void SetOnClick(ClickHandler handler);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
protected:
void InvalidateTextLayoutCache();
virtual void OnClick();
virtual D2D1_COLOR_F GetFillColor() const;
virtual D2D1_COLOR_F GetStrokeColor() const;
virtual D2D1_COLOR_F GetTextColor() const;
std::wstring text_;
float preferredHeight_ = 44.0f;
bool focused_ = false;
bool pressed_ = false;
ClickHandler onClick_;
std::wstring cachedFontFamily_;
float cachedLayoutWidth_ = -1.0f;
Microsoft::WRL::ComPtr<IDWriteTextLayout> cachedTextLayout_;
};
class Popup : public Visual
{
public:
explicit Popup(std::shared_ptr<Visual> child);
void SetAnchorRect(const RectF &anchorRect);
void SetPlacement(PopupPlacement placement);
void SetOffset(float x, float y);
void SetMatchAnchorWidth(bool matchAnchorWidth);
void SetConstrainToViewport(bool constrainToViewport);
void SetBackgroundFill(const D2D1_COLOR_F &fill);
void SetBorderColor(const D2D1_COLOR_F &border);
void SetCornerRadius(float radius);
void SetShadowEnabled(bool enabled);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
void Attach(Window *window) override;
Visual *FindVisualAt(const PointF &point) override;
Visual *FindFocusableAt(const PointF &point) override;
Visual *FindFirstFocusableDescendant() override;
bool HitTest(const PointF &point) const override;
bool KeepsPopupsOpenOnClick() const override;
void LayoutOverlay(const SizeF &viewportSize) override;
private:
std::shared_ptr<Visual> child_;
RectF anchorRect_ = {};
PopupPlacement placement_ = PopupPlacement::BelowLeading;
float offsetX_ = 0.0f;
float offsetY_ = 8.0f;
bool matchAnchorWidth_ = true;
bool constrainToViewport_ = true;
D2D1_COLOR_F backgroundFill_ = D2D1::ColorF(0xFFFFFF);
D2D1_COLOR_F borderColor_ = D2D1::ColorF(0xD6DCE5);
float cornerRadius_ = 16.0f;
bool shadowEnabled_ = false;
};
class MenuFlyoutItem : public Visual
{
public:
using ClickHandler = std::function<void()>;
using HoverHandler = std::function<void(bool hovered)>;
explicit MenuFlyoutItem(std::wstring text, bool hasSubmenu = false);
void SetOnClick(ClickHandler handler);
void SetOnHover(HoverHandler handler);
void SetColors(const D2D1_COLOR_F &text, const D2D1_COLOR_F &hoverFill);
void SetHasSubmenu(bool hasSubmenu);
void SetLeadingSvg(std::string svgUtf8);
void SetTrailingToggle(bool show);
void SetToggleOn(bool on);
bool IsToggleOn() const;
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
bool OnMouseMove(const POINT &point, WPARAM keyState) override;
void OnMouseLeave() override;
bool KeepsPopupsOpenOnClick() const override;
HCURSOR GetCursor() const override;
private:
void SetHovered(bool hovered);
bool UsesTrayLayout() const;
RectF ToggleHitRect() const;
std::wstring text_;
std::string leadingSvg_;
bool showToggle_ = false;
bool toggleOn_ = false;
bool hasSubmenu_ = false;
bool hovered_ = false;
bool pressed_ = false;
D2D1_COLOR_F textColor_ = D2D1::ColorF(0xE9E8E8);
D2D1_COLOR_F hoverFill_ = D2D1::ColorF(0x414141);
ClickHandler onClick_;
HoverHandler onHover_;
Microsoft::WRL::ComPtr<IDWriteTextLayout> textLayout_;
Microsoft::WRL::ComPtr<IUnknown> svgDocument_;
std::wstring cachedFontFamily_;
float cachedLayoutWidth_ = -1.0f;
void *svgContext_ = nullptr;
std::string svgTintCache_;
};
class MenuSeparator : public Visual
{
public:
void SetColor(const D2D1_COLOR_F &color);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
private:
D2D1_COLOR_F color_ = D2D1::ColorF(1.0f, 1.0f, 1.0f, 0.125f);
};
class PopupHost : public Visual
{
public:
PopupHost(std::shared_ptr<Visual> trigger, std::shared_ptr<Popup> popup);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
void Attach(Window *window) override;
Visual *FindVisualAt(const PointF &point) override;
Visual *FindFocusableAt(const PointF &point) override;
Visual *FindFirstFocusableDescendant() override;
bool HitTest(const PointF &point) const override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
bool KeepsPopupsOpenOnClick() const override;
void OpenPopup();
void ClosePopup();
void TogglePopup();
bool IsOpen() const;
private:
std::shared_ptr<Visual> trigger_;
std::shared_ptr<Popup> popup_;
bool pressed_ = false;
bool open_ = false;
};
class ContextMenuHost : public Visual
{
public:
ContextMenuHost(std::shared_ptr<Visual> trigger, std::shared_ptr<Popup> popup);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
void Attach(Window *window) override;
Visual *FindVisualAt(const PointF &point) override;
Visual *FindFocusableAt(const PointF &point) override;
Visual *FindFirstFocusableDescendant() override;
bool HitTest(const PointF &point) const override;
bool OnContextMenu(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
bool KeepsPopupsOpenOnClick() const override;
void OpenPopupAt(const PointF &anchorPoint);
void ClosePopup();
bool IsOpen() const;
private:
std::shared_ptr<Visual> trigger_;
std::shared_ptr<Popup> popup_;
bool open_ = false;
};
class ComboBox : public Visual
{
public:
using SelectionChangedHandler = std::function<void(size_t selectedIndex, const std::wstring &value)>;
explicit ComboBox(float height = 44.0f);
void AddItem(std::wstring item);
void ClearItems();
void SetSelectedIndex(size_t index);
size_t GetSelectedIndex() const;
const std::wstring &GetSelectedText() const;
void SetOnSelectionChanged(SelectionChangedHandler handler);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
void Attach(Window *window) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
bool KeepsPopupsOpenOnClick() const override;
private:
void OpenPopup();
void ClosePopup();
void TogglePopup();
void NotifySelectionChanged();
void SyncPopupState();
std::vector<std::wstring> items_;
float preferredHeight_ = 44.0f;
bool focused_ = false;
bool pressed_ = false;
bool open_ = false;
size_t selectedIndex_ = static_cast<size_t>(-1);
SelectionChangedHandler onSelectionChanged_;
std::shared_ptr<Visual> popupContent_;
std::shared_ptr<Popup> popup_;
};
class CheckBox : public Visual
{
public:
using ChangeHandler = std::function<void(bool checked)>;
CheckBox(std::wstring text, bool checked = false);
void SetOnChanged(ChangeHandler handler);
bool IsChecked() const;
void SetChecked(bool checked);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
private:
std::wstring text_;
bool checked_ = false;
bool focused_ = false;
bool pressed_ = false;
ChangeHandler onChanged_;
};
class ProgressBar : public Visual
{
public:
explicit ProgressBar(float height = 12.0f);
void SetValue(float value);
float GetValue() const;
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
private:
float preferredHeight_ = 12.0f;
float value_ = 0.0f;
};
class Slider : public Visual
{
public:
using ChangeHandler = std::function<void(float value)>;
Slider(float minValue, float maxValue, float value, float height = 34.0f);
void SetOnChanged(ChangeHandler handler);
void SetValue(float value);
float GetValue() const;
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
bool OnMouseMove(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
private:
float NormalizedValue() const;
void SetValueInternal(float value, bool notify);
bool UpdateFromPoint(const POINT &point, bool notify);
float minValue_ = 0.0f;
float maxValue_ = 100.0f;
float value_ = 0.0f;
float preferredHeight_ = 34.0f;
bool focused_ = false;
bool dragging_ = false;
ChangeHandler onChanged_;
};
class Separator : public Visual
{
public:
explicit Separator(float height = 1.0f);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
private:
float height_ = 1.0f;
};
class ListView : public Visual
{
public:
struct Item
{
std::wstring title;
std::wstring subtitle;
std::wstring badge;
};
using SelectionChangedHandler = std::function<void(size_t selectedIndex)>;
explicit ListView(float itemHeight = 68.0f);
void AddItem(Item item);
void ClearItems();
void SetOnSelectionChanged(SelectionChangedHandler handler);
void SetSelectedIndex(size_t index);
size_t GetSelectedIndex() const;
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
private:
struct ItemLayoutCache
{
float titleWidth = -1.0f;
float subtitleWidth = -1.0f;
float badgeWidth = -1.0f;
std::wstring fontFamily;
Microsoft::WRL::ComPtr<IDWriteTextLayout> titleLayout;
Microsoft::WRL::ComPtr<IDWriteTextLayout> subtitleLayout;
Microsoft::WRL::ComPtr<IDWriteTextLayout> badgeLayout;
};
void InvalidateLayoutCache();
size_t HitTestItem(const PointF &point) const;
std::vector<Item> items_;
std::vector<ItemLayoutCache> layoutCache_;
float itemHeight_ = 68.0f;
bool focused_ = false;
bool pressed_ = false;
size_t pressedIndex_ = static_cast<size_t>(-1);
size_t selectedIndex_ = 0;
SelectionChangedHandler onSelectionChanged_;
};
class CandidateList : public Visual
{
public:
struct Item
{
std::wstring label;
std::wstring text;
std::wstring annotation;
std::wstring translation;
};
enum class Orientation
{
Vertical,
Horizontal,
};
struct Appearance
{
float itemHeight = 28.0f;
float itemGap = 2.0f;
float fontSize = 16.0f;
float labelFontSize = 12.8f;
float annotationFontSize = 16.0f;
float cornerRadius = 4.0f;
float contentPadLeft = 5.0f;
float contentPadRight = 5.0f;
float textPadLeft = 0.0f;
float labelGap = 1.5f;
float selectedBarWidth = 3.0f;
float selectedBarHeight = 12.8f;
bool showSelectedBar = true;
D2D1_COLOR_F rowFillHover = D2D1::ColorF(0x343434);
D2D1_COLOR_F rowFillPressed = D2D1::ColorF(0x353535);
D2D1_COLOR_F rowFillSelected = D2D1::ColorF(0x3E3E3E, 0.725f);
D2D1_COLOR_F selectedBarColor = D2D1::ColorF(0x6B69D6);
D2D1_COLOR_F labelColor = D2D1::ColorF(0xE9E8E8, 0.616f);
D2D1_COLOR_F textColor = D2D1::ColorF(0xE9E8E8);
D2D1_COLOR_F annotationColor = D2D1::ColorF(0xE9E8E8);
};
using SelectionChangedHandler = std::function<void(size_t selectedIndex)>;
using ItemActivatedHandler = std::function<void(size_t selectedIndex)>;
using ContextMenuHandler = std::function<void(size_t selectedIndex, const POINT &clientPoint)>;
explicit CandidateList(float itemHeight = 40.0f);
void AddItem(Item item);
void SetItems(std::vector<Item> items);
void ClearItems();
void SetSelectedIndex(size_t index);
size_t GetSelectedIndex() const;
const Item *GetItem(size_t index) const;
void SetOnSelectionChanged(SelectionChangedHandler handler);
void SetOnItemActivated(ItemActivatedHandler handler);
void SetOnContextMenu(ContextMenuHandler handler);
void SetAppearance(Appearance appearance);
void SetOrientation(Orientation orientation);
void SetHoverEnabled(bool enabled);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
bool OnMouseMove(const POINT &point, WPARAM keyState) override;
bool OnContextMenu(const POINT &point, WPARAM keyState) override;
void OnMouseLeave() override;
bool OnKeyDown(WPARAM key, LPARAM lParam) override;
HCURSOR GetCursor() const override;
private:
struct ItemLayoutCache
{
float labelWidth = -1.0f;
float textWidth = -1.0f;
float annotationWidth = -1.0f;
float translationWidth = -1.0f;
std::wstring fontFamily;
Microsoft::WRL::ComPtr<IDWriteTextLayout> labelLayout;
Microsoft::WRL::ComPtr<IDWriteTextLayout> textLayout;
Microsoft::WRL::ComPtr<IDWriteTextLayout> annotationLayout;
Microsoft::WRL::ComPtr<IDWriteTextLayout> translationLayout;
};
void InvalidateLayoutCache();
size_t HitTestItem(const PointF &point) const;
float EstimateTextWidth(const std::wstring &text, float fontSize) const;
RectF ItemRect(size_t index) const;
std::vector<Item> items_;
std::vector<ItemLayoutCache> layoutCache_;
std::vector<float> itemWidths_;
Appearance appearance_{};
Orientation orientation_ = Orientation::Vertical;
float itemHeight_ = 40.0f;
bool focused_ = false;
bool pressed_ = false;
bool hoverEnabled_ = true;
size_t pressedIndex_ = static_cast<size_t>(-1);
size_t hoveredIndex_ = static_cast<size_t>(-1);
size_t selectedIndex_ = 0;
SelectionChangedHandler onSelectionChanged_;
ItemActivatedHandler onItemActivated_;
ContextMenuHandler onContextMenu_;
};
class TreeView : public Visual
{
public:
struct Node
{
std::wstring title;
std::wstring subtitle;
bool expanded = true;
std::vector<Node> children;
};
using SelectionChangedHandler = std::function<void(const std::wstring &selectedTitle)>;
explicit TreeView(float itemHeight = 62.0f);
void AddRoot(Node node);
void Clear();
void SetOnSelectionChanged(SelectionChangedHandler handler);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
bool HitTest(const PointF &point) const override;
bool IsFocusable() const override;
void OnFocusChanged(bool focused) override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
private:
struct VisibleNode
{
Node *node = nullptr;
size_t depth = 0;
RectF rowRect = {};
RectF expanderRect = {};
float titleWidth = -1.0f;
float subtitleWidth = -1.0f;
std::wstring fontFamily;
Microsoft::WRL::ComPtr<IDWriteTextLayout> titleLayout;
Microsoft::WRL::ComPtr<IDWriteTextLayout> subtitleLayout;
};
void BuildVisibleNodes();
void AppendVisibleNodes(Node &node, size_t depth);
void InvalidateLayoutCache();
VisibleNode *HitTestVisibleNode(const PointF &point);
const VisibleNode *HitTestVisibleNode(const PointF &point) const;
void SelectNode(Node *node);
std::vector<Node> roots_;
std::vector<VisibleNode> visibleNodes_;
float itemHeight_ = 62.0f;
bool focused_ = false;
bool pressed_ = false;
Node *pressedNode_ = nullptr;
bool pressedExpander_ = false;
Node *selectedNode_ = nullptr;
SelectionChangedHandler onSelectionChanged_;
};
class TabControl : public Visual
{
public:
struct Tab
{
std::wstring title;
std::shared_ptr<Visual> content;
};
using SelectionChangedHandler = std::function<void(size_t selectedIndex)>;
explicit TabControl(float headerHeight = 46.0f);
void AddTab(std::wstring title, std::shared_ptr<Visual> content);
void ClearTabs();
void SetSelectedIndex(size_t index);
size_t GetSelectedIndex() const;
void SetOnSelectionChanged(SelectionChangedHandler handler);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
void Attach(Window *window) override;
Visual *FindVisualAt(const PointF &point) override;
Visual *FindFocusableAt(const PointF &point) override;
Visual *FindFirstFocusableDescendant() override;
bool HitTest(const PointF &point) const override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
private:
size_t HitTestHeader(const PointF &point) const;
std::vector<Tab> tabs_;
std::vector<RectF> headerRects_;
float headerHeight_ = 46.0f;
size_t selectedIndex_ = 0;
bool pressed_ = false;
size_t pressedIndex_ = static_cast<size_t>(-1);
SelectionChangedHandler onSelectionChanged_;
};
class Accordion : public Visual
{
public:
struct Section
{
std::wstring title;
std::shared_ptr<Visual> content;
bool expanded = true;
};
explicit Accordion(float headerHeight = 48.0f);
void AddSection(std::wstring title, std::shared_ptr<Visual> content, bool expanded = true);
void ClearSections();
void SetAllowMultipleExpanded(bool allowMultipleExpanded);
SizeF Measure(const SizeF &availableSize) override;
void Arrange(const RectF &finalRect) override;
void Render(DeviceResources &deviceResources) override;
void Attach(Window *window) override;
Visual *FindVisualAt(const PointF &point) override;
Visual *FindFocusableAt(const PointF &point) override;
Visual *FindFirstFocusableDescendant() override;
bool HitTest(const PointF &point) const override;
bool OnMouseDown(const POINT &point, WPARAM keyState) override;
bool OnMouseUp(const POINT &point, WPARAM keyState) override;
HCURSOR GetCursor() const override;
private:
void CollapseOtherSections(size_t keepExpandedIndex);
size_t HitTestHeader(const PointF &point) const;
std::vector<Section> sections_;
std::vector<RectF> headerRects_;
std::vector<RectF> contentRects_;
float headerHeight_ = 48.0f;
bool allowMultipleExpanded_ = true;
bool pressed_ = false;
size_t pressedIndex_ = static_cast<size_t>(-1);
};
} // namespace msimeui