aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CruUI/CruUI.vcxproj2
-rw-r--r--CruUI/CruUI.vcxproj.filters6
-rw-r--r--CruUI/application.cpp2
-rw-r--r--CruUI/application.h7
-rw-r--r--CruUI/graph/graph.cpp22
-rw-r--r--CruUI/graph/graph.h1
-rw-r--r--CruUI/ui/controls/text_block.cpp24
-rw-r--r--CruUI/ui/controls/text_block.h24
-rw-r--r--CruUI/ui/controls/text_box.cpp215
-rw-r--r--CruUI/ui/controls/text_box.h73
-rw-r--r--CruUI/ui/ui_base.h13
11 files changed, 345 insertions, 44 deletions
diff --git a/CruUI/CruUI.vcxproj b/CruUI/CruUI.vcxproj
index 3d15905b..84462410 100644
--- a/CruUI/CruUI.vcxproj
+++ b/CruUI/CruUI.vcxproj
@@ -168,6 +168,7 @@
<ClInclude Include="ui\controls\linear_layout.h" />
<ClInclude Include="ui\controls\margin_container.h" />
<ClInclude Include="ui\controls\text_block.h" />
+ <ClInclude Include="ui\controls\text_box.h" />
<ClInclude Include="ui\controls\toggle_button.h" />
<ClInclude Include="ui\events\ui_event.h" />
<ClInclude Include="ui\layout_base.h" />
@@ -181,6 +182,7 @@
<ClCompile Include="ui\animations\animation.cpp" />
<ClCompile Include="ui\controls\button.cpp" />
<ClCompile Include="ui\controls\margin_container.cpp" />
+ <ClCompile Include="ui\controls\text_box.cpp" />
<ClCompile Include="ui\layout_base.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="graph\graph.cpp" />
diff --git a/CruUI/CruUI.vcxproj.filters b/CruUI/CruUI.vcxproj.filters
index aea865eb..3e4538fc 100644
--- a/CruUI/CruUI.vcxproj.filters
+++ b/CruUI/CruUI.vcxproj.filters
@@ -75,6 +75,9 @@
<ClInclude Include="ui\controls\margin_container.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="ui\controls\text_box.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="application.cpp">
@@ -128,5 +131,8 @@
<ClCompile Include="ui\controls\margin_container.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="ui\controls\text_box.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
</Project> \ No newline at end of file
diff --git a/CruUI/application.cpp b/CruUI/application.cpp
index 1375ed99..2d949edb 100644
--- a/CruUI/application.cpp
+++ b/CruUI/application.cpp
@@ -104,6 +104,8 @@ namespace cru {
#ifdef CRU_DEBUG_DRAW_CONTROL_BORDER
debug_border_brush_ = graph::CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Crimson));
#endif
+
+ caret_blink_duration_ = std::chrono::milliseconds(::GetCaretBlinkTime());
}
Application::~Application()
diff --git a/CruUI/application.h b/CruUI/application.h
index 1ce6f035..521c3436 100644
--- a/CruUI/application.h
+++ b/CruUI/application.h
@@ -101,6 +101,11 @@ namespace cru
return god_window_.get();
}
+ std::chrono::milliseconds GetCaretBlinkDuration() const
+ {
+ return caret_blink_duration_;
+ }
+
#ifdef CRU_DEBUG_DRAW_CONTROL_BORDER
Microsoft::WRL::ComPtr<ID2D1Brush> GetDebugBorderBrush() const
{
@@ -121,6 +126,8 @@ namespace cru
#ifdef CRU_DEBUG_DRAW_CONTROL_BORDER
Microsoft::WRL::ComPtr<ID2D1Brush> debug_border_brush_;
#endif
+
+ std::chrono::milliseconds caret_blink_duration_;
};
diff --git a/CruUI/graph/graph.cpp b/CruUI/graph/graph.cpp
index 94c7029e..30b51413 100644
--- a/CruUI/graph/graph.cpp
+++ b/CruUI/graph/graph.cpp
@@ -216,10 +216,30 @@ namespace cru {
ComPtr<ID2D1SolidColorBrush> CreateSolidBrush(const D2D1_COLOR_F& color)
{
- const auto device_context = graph::GraphManager::GetInstance()->GetD2D1DeviceContext();
+ const auto device_context = GraphManager::GetInstance()->GetD2D1DeviceContext();
ComPtr<ID2D1SolidColorBrush> solid_color_brush;
device_context->CreateSolidColorBrush(color, &solid_color_brush);
return solid_color_brush;
}
+
+ ComPtr<IDWriteTextFormat> CreateDefaultTextFormat()
+ {
+ const auto dwrite_factory = GraphManager::GetInstance()->GetDWriteFactory();
+
+ ComPtr<IDWriteTextFormat> text_format;
+
+ ThrowIfFailed(dwrite_factory->CreateTextFormat(
+ L"等线", nullptr,
+ DWRITE_FONT_WEIGHT_NORMAL,
+ DWRITE_FONT_STYLE_NORMAL,
+ DWRITE_FONT_STRETCH_NORMAL,
+ 24.0, L"zh-cn",
+ &text_format
+ ));
+
+ ThrowIfFailed(text_format->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
+ ThrowIfFailed(text_format->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
+ return text_format;
+ }
}
}
diff --git a/CruUI/graph/graph.h b/CruUI/graph/graph.h
index 01d95797..0f1d29d1 100644
--- a/CruUI/graph/graph.h
+++ b/CruUI/graph/graph.h
@@ -166,6 +166,7 @@ namespace cru
}
Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> CreateSolidBrush(const D2D1_COLOR_F& color);
+ Microsoft::WRL::ComPtr<IDWriteTextFormat> CreateDefaultTextFormat();
inline void WithTransform(ID2D1DeviceContext* device_context, const D2D1_MATRIX_3X2_F matrix, Function<void(ID2D1DeviceContext*)>&& action)
{
diff --git a/CruUI/ui/controls/text_block.cpp b/CruUI/ui/controls/text_block.cpp
index 8800fd91..f0ef41d9 100644
--- a/CruUI/ui/controls/text_block.cpp
+++ b/CruUI/ui/controls/text_block.cpp
@@ -21,8 +21,11 @@ namespace cru
const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) : Control(false)
{
text_format_ = init_text_format;
+
if (init_brush == nullptr)
brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Black));
+ else
+ brush_ = init_brush;
selection_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::LightSkyBlue));
}
@@ -60,7 +63,7 @@ namespace cru
void TextBlock::RemoveTextLayoutHandler(const TextLayoutHandlerPtr& handler)
{
const auto find_result = std::find(text_layout_handlers_.cbegin(), text_layout_handlers_.cend(),
- handler);
+ handler);
if (find_result != text_layout_handlers_.cend())
text_layout_handlers_.erase(find_result);
}
@@ -259,23 +262,6 @@ namespace cru
Repaint();
}
- void TextBlock::CreateDefaultTextFormat()
- {
- const auto dwrite_factory = GetDWriteFactory();
-
- ThrowIfFailed(dwrite_factory->CreateTextFormat(
- L"µÈÏß", nullptr,
- DWRITE_FONT_WEIGHT_NORMAL,
- DWRITE_FONT_STYLE_NORMAL,
- DWRITE_FONT_STRETCH_NORMAL,
- 24.0, L"zh-cn",
- &text_format_
- ));
-
- ThrowIfFailed(text_format_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER));
- ThrowIfFailed(text_format_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER));
- }
-
void TextBlock::RecreateTextLayout()
{
if (text_.empty())
@@ -287,7 +273,7 @@ namespace cru
const auto dwrite_factory = GetDWriteFactory();
if (text_format_ == nullptr)
- CreateDefaultTextFormat();
+ text_format_ = graph::CreateDefaultTextFormat();
const auto&& size = GetSize();
diff --git a/CruUI/ui/controls/text_block.h b/CruUI/ui/controls/text_block.h
index b05e7ff2..c87ffc51 100644
--- a/CruUI/ui/controls/text_block.h
+++ b/CruUI/ui/controls/text_block.h
@@ -11,29 +11,6 @@ namespace cru
{
namespace controls
{
- struct TextRange
- {
- TextRange() = default;
- TextRange(const int position, const int count)
- : position(position), count(count)
- {
-
- }
- TextRange(const TextRange& other) = default;
- TextRange(TextRange&& other) = default;
- TextRange& operator=(const TextRange& other) = default;
- TextRange& operator=(TextRange&& other) = default;
- ~TextRange() = default;
-
- unsigned position;
- unsigned count;
-
- explicit operator DWRITE_TEXT_RANGE() const
- {
- return DWRITE_TEXT_RANGE { position, count };
- }
- };
-
class TextBlock : public Control
{
public:
@@ -116,7 +93,6 @@ namespace cru
private:
void OnTextChangedCore(const String& old_text, const String& new_text);
- void CreateDefaultTextFormat();
void RecreateTextLayout();
private:
diff --git a/CruUI/ui/controls/text_box.cpp b/CruUI/ui/controls/text_box.cpp
new file mode 100644
index 00000000..ce834ef6
--- /dev/null
+++ b/CruUI/ui/controls/text_box.cpp
@@ -0,0 +1,215 @@
+#include "text_box.h"
+
+#include "graph/graph.h"
+#include "exception.h"
+
+namespace cru::ui::controls
+{
+ using graph::CreateSolidBrush;
+
+ inline Microsoft::WRL::ComPtr<IDWriteFactory> GetDWriteFactory()
+ {
+ return graph::GraphManager::GetInstance()->GetDWriteFactory();
+ }
+
+ TextBox::TextBox(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format,
+ const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) : Control(false)
+ {
+ text_format_ = init_text_format;
+
+ if (init_brush == nullptr)
+ brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Black));
+ else
+ brush_ = init_brush;
+
+ caret_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::Black));
+
+ //selection_brush_ = CreateSolidBrush(D2D1::ColorF(D2D1::ColorF::LightSkyBlue));
+ }
+
+ TextBox::~TextBox() = default;
+
+ void TextBox::SetText(const String& text)
+ {
+ if (text_ != text)
+ {
+ const auto old_text = text_;
+ text_ = text;
+ OnTextChangedCore(old_text, text);
+ }
+ }
+
+ void TextBox::SetBrush(const Microsoft::WRL::ComPtr<ID2D1Brush>& brush)
+ {
+ brush_ = brush;
+ Repaint();
+ }
+
+ void TextBox::SetTextFormat(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& text_format)
+ {
+ text_format_ = text_format;
+ RecreateTextLayout();
+ Repaint();
+ }
+
+ void TextBox::OnSizeChangedCore(events::SizeChangedEventArgs& args)
+ {
+ Control::OnSizeChangedCore(args);
+ text_layout_->SetMaxWidth(args.GetNewSize().width);
+ text_layout_->SetMaxHeight(args.GetNewSize().height);
+ Repaint();
+ }
+
+ void TextBox::OnDraw(ID2D1DeviceContext* device_context)
+ {
+ Control::OnDraw(device_context);
+ if (text_layout_ != nullptr)
+ {
+ //if (selected_range_.has_value())
+ //{
+ // DWRITE_TEXT_METRICS text_metrics{};
+ // ThrowIfFailed(text_layout_->GetMetrics(&text_metrics));
+ // const auto metrics_count = text_metrics.lineCount * text_metrics.maxBidiReorderingDepth;
+
+ // Vector<DWRITE_HIT_TEST_METRICS> hit_test_metrics(metrics_count);
+ // UINT32 actual_count;
+ // text_layout_->HitTestTextRange(
+ // selected_range_.value().position, selected_range_.value().count,
+ // 0, 0,
+ // hit_test_metrics.data(), metrics_count, &actual_count
+ // );
+
+ // hit_test_metrics.erase(hit_test_metrics.cbegin() + actual_count, hit_test_metrics.cend());
+
+ // for (const auto& metrics : hit_test_metrics)
+ // {
+ // device_context->FillRoundedRectangle(D2D1::RoundedRect(D2D1::RectF(metrics.left, metrics.top, metrics.left + metrics.width, metrics.top + metrics.height), 3, 3), selection_brush_.Get());
+ // }
+ //}
+ device_context->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get());
+ }
+ }
+
+ std::optional<unsigned> TextLayoutHitTest(IDWriteTextLayout* text_layout, const Point& point, bool test_inside = true)
+ {
+ BOOL is_trailing, is_inside;
+ DWRITE_HIT_TEST_METRICS metrics{};
+ text_layout->HitTestPoint(point.x, point.y, &is_trailing, &is_inside, &metrics);
+ if (!test_inside || is_inside)
+ return is_trailing == 0 ? metrics.textPosition : metrics.textPosition + 1;
+ else
+ return std::nullopt;
+ }
+
+ void TextBox::OnMouseDownCore(events::MouseButtonEventArgs& args)
+ {
+ Control::OnMouseDownCore(args);
+ if (args.GetMouseButton() == MouseButton::Left)
+ {
+ position_ = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this), false).value();
+
+ Repaint();
+ }
+ }
+
+ void TextBox::OnGetFocusCore(events::FocusChangeEventArgs& args)
+ {
+ Control::OnGetFocusCore(args);
+ assert(caret_timer_ == nullptr);
+ caret_timer_ = SetInterval(Application::GetInstance()->GetCaretBlinkDuration(), caret_action_);
+ }
+
+ void TextBox::OnLoseFocusCore(events::FocusChangeEventArgs& args)
+ {
+ Control::OnLoseFocusCore(args);
+ assert(caret_timer_ != nullptr);
+ caret_timer_->Cancel();
+ }
+
+ Size TextBox::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;
+
+ auto&& get_measure_length = [](const LayoutSideParams& 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();
+ }
+ };
+
+ const Size measure_size(get_measure_length(layout_params->width, available_size.width),
+ get_measure_length(layout_params->height, available_size.height));
+
+ ThrowIfFailed(text_layout_->SetMaxWidth(measure_size.width));
+ ThrowIfFailed(text_layout_->SetMaxHeight(measure_size.height));
+
+ DWRITE_TEXT_METRICS metrics{};
+
+ ThrowIfFailed(text_layout_->GetMetrics(&metrics));
+
+ const Size measure_result(metrics.width, metrics.height);
+
+ auto&& calculate_final_length = [](const LayoutSideParams& 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;
+ };
+
+ const Size result_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)
+ );
+
+ return result_size;
+ }
+
+ void TextBox::OnTextChangedCore(const String& old_text, const String& new_text)
+ {
+ RecreateTextLayout();
+ Repaint();
+ }
+
+ void TextBox::RecreateTextLayout()
+ {
+ if (text_.empty())
+ {
+ text_layout_ = nullptr;
+ return;
+ }
+
+ const auto dwrite_factory = GetDWriteFactory();
+
+ if (text_format_ == nullptr)
+ text_format_ = graph::CreateDefaultTextFormat();
+
+ const auto&& size = GetSize();
+
+ ThrowIfFailed(dwrite_factory->CreateTextLayout(
+ text_.c_str(), static_cast<UINT32>(text_.size()),
+ text_format_.Get(),
+ size.width, size.height,
+ &text_layout_
+ ));
+ }
+}
diff --git a/CruUI/ui/controls/text_box.h b/CruUI/ui/controls/text_box.h
new file mode 100644
index 00000000..68235c67
--- /dev/null
+++ b/CruUI/ui/controls/text_box.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include "ui/control.h"
+#include "timer.h"
+
+namespace cru::ui::controls
+{
+ class TextBox : public Control
+ {
+ protected:
+ explicit TextBox(
+ const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format = nullptr,
+ const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush = nullptr
+ );
+ public:
+ TextBox(const TextBox& other) = delete;
+ TextBox(TextBox&& other) = delete;
+ TextBox& operator=(const TextBox& other) = delete;
+ TextBox& operator=(TextBox&& other) = delete;
+ ~TextBox() override;
+
+ String GetText() const
+ {
+ return text_;
+ }
+
+ void SetText(const String& text);
+
+ Microsoft::WRL::ComPtr<ID2D1Brush> GetBrush() const
+ {
+ return brush_;
+ }
+
+ void SetBrush(const Microsoft::WRL::ComPtr<ID2D1Brush>& brush);
+
+ Microsoft::WRL::ComPtr<IDWriteTextFormat> GetTextFormat() const
+ {
+ return text_format_;
+ }
+
+ void SetTextFormat(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& text_format);
+
+ protected:
+ void OnSizeChangedCore(events::SizeChangedEventArgs& args) override final;
+ void OnDraw(ID2D1DeviceContext* device_context) override;
+
+ void OnMouseDownCore(events::MouseButtonEventArgs& args) override final;
+
+ void OnGetFocusCore(events::FocusChangeEventArgs& args) override;
+ void OnLoseFocusCore(events::FocusChangeEventArgs& args) override final;
+
+ Size OnMeasure(const Size& available_size) override final;
+
+ private:
+ void OnTextChangedCore(const String& old_text, const String& new_text);
+
+ void RecreateTextLayout();
+
+ private:
+ String text_;
+
+ Microsoft::WRL::ComPtr<ID2D1Brush> brush_;
+ Microsoft::WRL::ComPtr<ID2D1Brush> caret_brush_;
+ //Microsoft::WRL::ComPtr<ID2D1Brush> selection_brush_;
+ Microsoft::WRL::ComPtr<IDWriteTextFormat> text_format_;
+ Microsoft::WRL::ComPtr<IDWriteTextLayout> text_layout_;
+
+ unsigned position_ = 0;
+
+ TimerTask caret_timer_;
+ ActionPtr caret_action_;
+ };
+}
diff --git a/CruUI/ui/ui_base.h b/CruUI/ui/ui_base.h
index c9ae6017..43f3c498 100644
--- a/CruUI/ui/ui_base.h
+++ b/CruUI/ui/ui_base.h
@@ -137,5 +137,18 @@ namespace cru
Right,
Middle
};
+
+ struct TextRange
+ {
+ constexpr TextRange() = default;
+ constexpr TextRange(const int position, const int count)
+ : position(position), count(count)
+ {
+
+ }
+
+ unsigned position = 0;
+ unsigned count = 0;
+ };
}
}