ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
Label.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <imgui.h>
11 
12 #include <algorithm>
13 #include <cmath>
14 #include <string>
15 
18 #include "visualization/gui/Util.h"
19 
20 namespace cloudViewer {
21 namespace visualization {
22 namespace gui {
23 
24 // If word-wrapping is enabled, there isn't a preferred size, per se.
25 // But we don't want to make the preferred width too long, or it gets hard to
26 // read. Somewhere between 60 and 90 characters is the max. Proportional
27 // width fonts have approximately 2.5 characters per em. So the width should
28 // be something like 24 - 36 em.
29 // See https://practicaltypography.com/line-length.html
30 // and https://pearsonified.com/characters-per-line/
31 static const int PREFERRED_WRAP_WIDTH_EM = 35;
32 
33 static const Color DEFAULT_COLOR(0, 0, 0, 0);
34 
35 struct Label::Impl {
36  std::string text_;
39  bool is_single_line = true;
40 };
41 
42 Label::Label(const char* text /*= nullptr*/) : impl_(new Label::Impl()) {
43  if (text) {
44  SetText(text);
45  }
46 }
47 
49 
50 const char* Label::GetText() const { return impl_->text_.c_str(); }
51 
52 void Label::SetText(const char* text) {
53  impl_->text_ = text;
54  impl_->is_single_line = !(impl_->text_.find('\n') != std::string::npos);
55 }
56 
57 Color Label::GetTextColor() const { return impl_->color_; }
58 
59 void Label::SetTextColor(const Color& color) { impl_->color_ = color; }
60 
61 FontId Label::GetFontId() const { return impl_->font_id_; }
62 
63 void Label::SetFontId(const FontId font_id) { impl_->font_id_ = font_id; }
64 
66  const Constraints& constraints) const {
67  ImGui::PushFont((ImFont*)context.fonts.GetFont(impl_->font_id_));
68 
69  auto padding = ImGui::GetStyle().FramePadding;
70  auto* font = ImGui::GetFont();
71  Size pref;
72 
73  if (impl_->is_single_line) {
74  float wrap_width = float(constraints.width);
75  auto size =
76  font->CalcTextSizeA(font->FontSize, float(constraints.width),
77  wrap_width, impl_->text_.c_str());
78  pref = Size(int(std::ceil(size.x + 2.0f * padding.x)),
79  int(std::ceil(size.y + 2.0f * padding.y)));
80  } else {
81  ImVec2 size(0, 0);
82  size_t line_start = 0;
83  auto line_end = impl_->text_.find('\n');
84  auto em = int(std::round(font->FontSize));
85  float wrap_width = float(
86  std::min(constraints.width, PREFERRED_WRAP_WIDTH_EM * em));
87  float spacing = ImGui::GetTextLineHeightWithSpacing() -
88  ImGui::GetTextLineHeight();
89  do {
90  ImVec2 sz;
91  if (line_end == std::string::npos) {
92  sz = font->CalcTextSizeA(font->FontSize, FLT_MAX, wrap_width,
93  impl_->text_.c_str() + line_start);
94  line_start = line_end;
95  } else {
96  sz = font->CalcTextSizeA(font->FontSize, FLT_MAX, wrap_width,
97  impl_->text_.c_str() + line_start,
98  impl_->text_.c_str() + line_end);
99  line_start = line_end + 1;
100  line_end = impl_->text_.find('\n', line_start);
101  }
102  size.x = std::max(size.x, sz.x);
103  size.y += sz.y + spacing;
104  } while (line_start != std::string::npos);
105 
106  pref = Size(int(std::ceil(size.x)) + int(std::ceil(2.0f * padding.x)),
107  int(std::ceil(size.y - spacing)) +
108  int(std::ceil(2.0f * padding.y)));
109  }
110 
111  ImGui::PopFont();
112  return pref;
113 }
114 
116  auto& frame = GetFrame();
117  ImGui::SetCursorScreenPos(
118  ImVec2(float(frame.x), float(frame.y) - ImGui::GetScrollY()));
119  ImGui::PushItemWidth(float(frame.width));
120  bool is_default_color = (impl_->color_ == DEFAULT_COLOR);
121  if (!is_default_color) {
122  ImGui::PushStyleColor(ImGuiCol_Text, colorToImgui(impl_->color_));
123  }
124  ImGui::PushFont((ImFont*)context.fonts.GetFont(impl_->font_id_));
125 
126  auto padding = ImGui::GetStyle().FramePadding;
127  float wrapX = ImGui::GetCursorPos().x + frame.width - padding.x;
128  ImGui::PushTextWrapPos(wrapX);
129  ImGui::TextWrapped("%s", impl_->text_.c_str());
130  ImGui::PopTextWrapPos();
131 
132  ImGui::PopFont();
133  if (!is_default_color) {
134  ImGui::PopStyleColor();
135  }
136  ImGui::PopItemWidth();
137  // Tooltip (if it exists) is in the system font, so do after popping font
140 }
141 
142 } // namespace gui
143 } // namespace visualization
144 } // namespace cloudViewer
Rect frame
int size
math::float4 color
static constexpr FontId DEFAULT_FONT_ID
Identifier for font used by default for all UI elements.
Definition: Application.h:57
const char * GetText() const
Definition: Label.cpp:50
void SetText(const char *text)
Sets the text of the label (copies text)
Definition: Label.cpp:52
void SetTextColor(const Color &color)
Definition: Label.cpp:59
DrawResult Draw(const DrawContext &context) override
Definition: Label.cpp:115
Label(const char *text=nullptr)
Copies text.
Definition: Label.cpp:42
Size CalcPreferredSize(const LayoutContext &context, const Constraints &constraints) const override
Definition: Label.cpp:65
void SetFontId(const FontId font_id)
Definition: Label.cpp:63
virtual const Rect & GetFrame() const
Returns the frame size in pixels.
Definition: Widget.cpp:51
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
ImGuiContext * context
Definition: Window.cpp:76
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
static const int PREFERRED_WRAP_WIDTH_EM
Definition: Label.cpp:31
static const Color DEFAULT_COLOR(0, 0, 0, 0)
ImVec4 colorToImgui(const Color &color)
Definition: Util.cpp:20
Generic file read and write utility for python interface.