diff options
-rw-r--r-- | CruUI/ui/control.cpp | 12 | ||||
-rw-r--r-- | CruUI/ui/control.h | 15 | ||||
-rw-r--r-- | CruUI/ui/controls/text_block.cpp | 73 | ||||
-rw-r--r-- | CruUI/ui/controls/text_block.h | 19 |
4 files changed, 113 insertions, 6 deletions
diff --git a/CruUI/ui/control.cpp b/CruUI/ui/control.cpp index ac338e95..20aeb900 100644 --- a/CruUI/ui/control.cpp +++ b/CruUI/ui/control.cpp @@ -9,7 +9,8 @@ namespace cru { namespace ui { using namespace events; - Control::Control() : + Control::Control(bool container) : + is_container_(container), window_(nullptr), parent_(nullptr), children_(), @@ -26,12 +27,14 @@ namespace cru { void Control::ForeachChild(Action<Control*>&& predicate) const { + ThrowIfNotContainer(); for (const auto child : children_) predicate(child); } void Control::ForeachChild(FlowControlAction<Control*>&& predicate) const { + ThrowIfNotContainer(); for (const auto child : children_) { if (predicate(child) == FlowControl::Break) @@ -50,6 +53,7 @@ namespace cru { void Control::AddChild(Control* control) { + ThrowIfNotContainer(); AddChildCheck(control); this->children_.push_back(control); @@ -61,6 +65,7 @@ namespace cru { void Control::AddChild(Control* control, int position) { + ThrowIfNotContainer(); AddChildCheck(control); if (position < 0 || static_cast<decltype(this->children_.size())>(position) > this->children_.size()) @@ -75,6 +80,7 @@ namespace cru { void Control::RemoveChild(Control* child) { + ThrowIfNotContainer(); const auto i = std::find(this->children_.cbegin(), this->children_.cend(), child); if (i == this->children_.cend()) throw std::invalid_argument("The argument child is not a child of this control."); @@ -88,6 +94,7 @@ namespace cru { void Control::RemoveChild(const int position) { + ThrowIfNotContainer(); if (position < 0 || static_cast<decltype(this->children_.size())>(position) >= this->children_.size()) throw std::invalid_argument("The position is out of range."); @@ -123,6 +130,7 @@ namespace cru { void Control::TraverseDescendants(Action<Control*>&& predicate) { + ThrowIfNotContainer(); TraverseDescendantsInternal(this, predicate); } @@ -401,7 +409,7 @@ namespace cru { } }; - Size size_for_children{}; + Size size_for_children; size_for_children.width = get_available_length_for_child(layout_params->width, available_size.width); size_for_children.height = get_available_length_for_child(layout_params->height, available_size.height); diff --git a/CruUI/ui/control.h b/CruUI/ui/control.h index ee6abe12..b9dccf19 100644 --- a/CruUI/ui/control.h +++ b/CruUI/ui/control.h @@ -29,7 +29,7 @@ namespace cru friend class Window; friend class WindowLayoutManager; protected: - Control(); + explicit Control(bool container = false); public: Control(const Control& other) = delete; @@ -42,6 +42,11 @@ namespace cru //*************** region: tree *************** + bool IsContainer() const + { + return is_container_; + } + //Get parent of control, return nullptr if it has no parent. Control* GetParent() const { @@ -235,7 +240,15 @@ namespace cru // be done. void CheckAndNotifyPositionChanged(); + void ThrowIfNotContainer() const + { + if (!is_container_) + throw std::runtime_error("You can't perform such operation on a non-container control."); + } + private: + bool is_container_; + Window * window_; Control * parent_; diff --git a/CruUI/ui/controls/text_block.cpp b/CruUI/ui/controls/text_block.cpp index a3d7577f..05eb775e 100644 --- a/CruUI/ui/controls/text_block.cpp +++ b/CruUI/ui/controls/text_block.cpp @@ -10,6 +10,11 @@ namespace cru { namespace controls { + inline Microsoft::WRL::ComPtr<IDWriteFactory> GetDWriteFactory() + { + return graph::GraphManager::GetInstance()->GetDWriteFactory(); + } + TextBlock::TextBlock(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format, const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) { @@ -53,8 +58,65 @@ namespace cru Size TextBlock::OnMeasure(const Size& available_size) { + if (text_.empty()) + return Size::zero; + + const auto layout_params = GetLayoutParams(); + + + if (layout_params->width.mode == MeasureMode::Stretch && layout_params->height.mode == MeasureMode::Stretch) + return available_size; + + Size measure_size; + + auto&& get_measure_length = [](const MeasureLength& layout_length, const float available_length) -> float + { + switch (layout_length.mode) + { + case MeasureMode::Exactly: + { + return std::min(layout_length.length, available_length); + } + case MeasureMode::Stretch: + case MeasureMode::Content: + { + return available_length; + } + default: + UnreachableCode(); + } + }; + + measure_size.width = get_measure_length(layout_params->width, available_size.width); + measure_size.height = get_measure_length(layout_params->height, available_size.height); - //TODO! + + Microsoft::WRL::ComPtr<IDWriteTextLayout> measure_text_layout; + + const auto dwrite_factory = GetDWriteFactory(); + + dwrite_factory->CreateTextLayout(text_.c_str(), text_.size(), + text_format_.Get(), measure_size.width, measure_size.height, &measure_text_layout); + + DWRITE_TEXT_METRICS metrics{}; + measure_text_layout->GetMetrics(&metrics); + + const Size measure_result(metrics.width, metrics.height); + + auto&& calculate_final_length = [](const MeasureLength& layout_length, const float measure_length, const float measure_result_length) -> float + { + if ((layout_length.mode == MeasureMode::Stretch || + layout_length.mode == MeasureMode::Exactly) + && measure_result_length < measure_length) + return measure_length; + else + return measure_result_length; + }; + + return Size( + calculate_final_length(layout_params->width, measure_size.width, measure_result.width), + calculate_final_length(layout_params->height, measure_size.height, measure_result.height) + ); } void TextBlock::OnTextChangedCore(const String& old_text, const String& new_text) @@ -73,7 +135,7 @@ namespace cru void TextBlock::CreateDefaultTextFormat() { - const auto dwrite_factory = graph::GraphManager::GetInstance()->GetDWriteFactory(); + const auto dwrite_factory = GetDWriteFactory(); ThrowIfFailed(dwrite_factory->CreateTextFormat( L"΅ΘΟί", nullptr, @@ -93,7 +155,7 @@ namespace cru return; } - const auto dwrite_factory = graph::GraphManager::GetInstance()->GetDWriteFactory(); + const auto dwrite_factory = GetDWriteFactory(); if (text_format_ == nullptr) CreateDefaultTextFormat(); @@ -106,6 +168,11 @@ namespace cru size.width, size.height, &text_layout_ ); + + std::for_each(text_layout_handlers_.cbegin(), text_layout_handlers_.cend(), [this](const std::shared_ptr<TextLayoutHandler>& handler) + { + (*handler)(text_layout_); + }); } } } diff --git a/CruUI/ui/controls/text_block.h b/CruUI/ui/controls/text_block.h index 85bfe90c..abf77112 100644 --- a/CruUI/ui/controls/text_block.h +++ b/CruUI/ui/controls/text_block.h @@ -1,5 +1,7 @@ #pragma once +#include <memory> + #include "ui/control.h" namespace cru @@ -11,6 +13,9 @@ namespace cru class TextBlock : public Control { public: + using TextLayoutHandler = Action<Microsoft::WRL::ComPtr<IDWriteTextLayout>>; + + explicit TextBlock( const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format = nullptr, const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush = nullptr @@ -42,6 +47,18 @@ namespace cru void SetTextFormat(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& text_format); + + void AddTextLayoutHandler(std::shared_ptr<TextLayoutHandler> handler) + { + text_layout_handlers_.push_back(std::move(handler)); + } + void RemoveTextLayoutHandler(const std::shared_ptr<TextLayoutHandler>& handler) + { + const auto find_result = std::find(text_layout_handlers_.cbegin(), text_layout_handlers_.cend(), handler); + if (find_result != text_layout_handlers_.cend()) + text_layout_handlers_.erase(find_result); + } + protected: void OnSizeChangedCore(events::SizeChangedEventArgs& args) override final; void OnDraw(ID2D1DeviceContext* device_context) override; @@ -61,6 +78,8 @@ namespace cru Microsoft::WRL::ComPtr<ID2D1Brush> brush_; Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format_; Microsoft::WRL::ComPtr<IDWriteTextLayout> text_layout_; + + Vector<std::shared_ptr<TextLayoutHandler>> text_layout_handlers_; }; } } |