reference_lines.cc 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/hud_display/reference_lines.h"
  5. #include "ash/hud_display/hud_constants.h"
  6. #include "base/strings/stringprintf.h"
  7. #include "base/strings/utf_string_conversions.h"
  8. #include "cc/paint/paint_flags.h"
  9. #include "third_party/skia/include/core/SkBlendMode.h"
  10. #include "third_party/skia/include/core/SkPaint.h"
  11. #include "third_party/skia/include/core/SkPath.h"
  12. #include "third_party/skia/include/effects/SkDashPathEffect.h"
  13. #include "ui/base/metadata/metadata_impl_macros.h"
  14. #include "ui/gfx/canvas.h"
  15. #include "ui/gfx/text_constants.h"
  16. namespace ash {
  17. namespace hud_display {
  18. namespace {
  19. constexpr SkColor kHUDGraphReferenceLineColor = SkColorSetRGB(162, 162, 220);
  20. std::u16string GenerateLabelText(float value, const std::u16string& dimention) {
  21. if (value == (int)value) {
  22. return base::ASCIIToUTF16(base::StringPrintf("%d ", (int)value).c_str()) +
  23. dimention;
  24. } else {
  25. return base::ASCIIToUTF16(base::StringPrintf("%.2f ", value).c_str()) +
  26. dimention;
  27. }
  28. }
  29. } // anonymous namespace
  30. BEGIN_METADATA(ReferenceLines, views::View)
  31. END_METADATA
  32. ReferenceLines::ReferenceLines(float left,
  33. float top,
  34. float right,
  35. float bottom,
  36. const std::u16string& x_unit,
  37. const std::u16string& y_unit,
  38. int horizontal_points_number,
  39. int horizontal_ticks_interval,
  40. float vertical_ticks_interval)
  41. : color_(kHUDGraphReferenceLineColor),
  42. left_(left),
  43. top_(top),
  44. right_(right),
  45. bottom_(bottom),
  46. x_unit_(x_unit),
  47. y_unit_(y_unit),
  48. horizontal_points_number_(horizontal_points_number),
  49. horizontal_ticks_interval_(horizontal_ticks_interval),
  50. vertical_ticks_interval_(vertical_ticks_interval) {
  51. // Text is set later.
  52. right_top_label_ = AddChildView(std::make_unique<views::Label>(
  53. std::u16string(), views::style::CONTEXT_LABEL));
  54. right_middle_label_ = AddChildView(std::make_unique<views::Label>(
  55. std::u16string(), views::style::CONTEXT_LABEL));
  56. right_bottom_label_ = AddChildView(std::make_unique<views::Label>(
  57. std::u16string(), views::style::CONTEXT_LABEL));
  58. left_bottom_label_ = AddChildView(std::make_unique<views::Label>(
  59. std::u16string(), views::style::CONTEXT_LABEL));
  60. // Set label text.
  61. SetTopLabel(top_);
  62. SetBottomLabel(bottom_);
  63. SetLeftLabel(left_);
  64. right_top_label_->SetEnabledColor(color_);
  65. right_top_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  66. right_middle_label_->SetEnabledColor(color_);
  67. right_middle_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  68. right_bottom_label_->SetEnabledColor(color_);
  69. right_bottom_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  70. left_bottom_label_->SetEnabledColor(color_);
  71. left_bottom_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  72. }
  73. ReferenceLines::~ReferenceLines() = default;
  74. void ReferenceLines::Layout() {
  75. // Align all the right labels on their left edge.
  76. gfx::Size right_top_label_size = right_top_label_->GetPreferredSize();
  77. gfx::Size right_middle_label_size = right_middle_label_->GetPreferredSize();
  78. gfx::Size right_bottom_label_size = right_bottom_label_->GetPreferredSize();
  79. const int right_labels_width = std::max(
  80. right_top_label_size.width(), std::max(right_middle_label_size.width(),
  81. right_bottom_label_size.width()));
  82. right_top_label_size.set_width(right_labels_width);
  83. right_middle_label_size.set_width(right_labels_width);
  84. right_bottom_label_size.set_width(right_labels_width);
  85. right_top_label_->SetSize(right_top_label_size);
  86. right_middle_label_->SetSize(right_middle_label_size);
  87. right_bottom_label_->SetSize(right_bottom_label_size);
  88. left_bottom_label_->SetSize(left_bottom_label_->GetPreferredSize());
  89. constexpr int label_border = 3; // Offset to labels from the reference lines.
  90. const gfx::Point right_top_label_position(
  91. bounds().width() - right_top_label_size.width() - label_border,
  92. label_border);
  93. const gfx::Point right_middle_label_position(
  94. bounds().width() - right_middle_label_size.width() - label_border,
  95. bounds().height() / 2 - right_middle_label_size.height() - label_border);
  96. const gfx::Point right_bottom_label_position(
  97. bounds().width() - right_bottom_label_size.width() - label_border,
  98. bounds().height() - right_bottom_label_size.height() - label_border);
  99. right_top_label_->SetPosition(right_top_label_position);
  100. right_middle_label_->SetPosition(right_middle_label_position);
  101. right_bottom_label_->SetPosition(right_bottom_label_position);
  102. left_bottom_label_->SetPosition(
  103. {label_border, bounds().height() -
  104. left_bottom_label_->GetPreferredSize().height() -
  105. label_border});
  106. views::View::Layout();
  107. }
  108. void ReferenceLines::OnPaint(gfx::Canvas* canvas) {
  109. SkPath dotted_path;
  110. SkPath solid_path;
  111. // Draw dashed line at 50%.
  112. dotted_path.moveTo({0, bounds().height() / 2.0f});
  113. dotted_path.lineTo(
  114. {static_cast<SkScalar>(bounds().width()), bounds().height() / 2.0f});
  115. // Draw border and ticks.
  116. solid_path.addRect(SkRect::MakeXYWH(bounds().x(), bounds().y(),
  117. bounds().width(), bounds().height()));
  118. const SkScalar tick_length = 3;
  119. // Vertical interval ticks (drawn horizontally).
  120. if (vertical_ticks_interval_ > 0) {
  121. float tick_bottom_offset = vertical_ticks_interval_;
  122. while (tick_bottom_offset <= 1) {
  123. // Skip 50%.
  124. if (fabs(tick_bottom_offset - .5) > 0.01) {
  125. const SkScalar line_y = (1 - tick_bottom_offset) * bounds().height();
  126. solid_path.moveTo({0, line_y});
  127. solid_path.lineTo({tick_length, line_y});
  128. solid_path.moveTo({bounds().width() - tick_length, line_y});
  129. solid_path.lineTo({static_cast<SkScalar>(bounds().width()), line_y});
  130. }
  131. tick_bottom_offset += vertical_ticks_interval_;
  132. }
  133. }
  134. // Horizontal interval ticks (drawn vertically).
  135. if (horizontal_points_number_ > 0 && horizontal_ticks_interval_ > 0) {
  136. // Add one more tick if graph width is not a multiple of tick width.
  137. const int h_ticks =
  138. horizontal_points_number_ / horizontal_ticks_interval_ +
  139. (horizontal_points_number_ % horizontal_ticks_interval_ ? 1 : 0);
  140. // Interval between ticks in pixels.
  141. const SkScalar tick_per_pixels = bounds().width() /
  142. (float)horizontal_points_number_ *
  143. horizontal_ticks_interval_;
  144. for (int i = 1; i < h_ticks; ++i) {
  145. const SkScalar line_x = bounds().width() - tick_per_pixels * i;
  146. solid_path.moveTo({line_x, 0});
  147. solid_path.lineTo({line_x, tick_length});
  148. solid_path.moveTo({line_x, bounds().height() - tick_length});
  149. solid_path.lineTo({line_x, static_cast<SkScalar>(bounds().height())});
  150. }
  151. }
  152. cc::PaintFlags flags;
  153. flags.setAntiAlias(true);
  154. flags.setBlendMode(SkBlendMode::kSrc);
  155. flags.setStyle(cc::PaintFlags::kStroke_Style);
  156. flags.setStrokeWidth(kHUDGraphReferenceLineWidth);
  157. flags.setColor(color_);
  158. canvas->DrawPath(solid_path, flags);
  159. const SkScalar intervals[] = {5, 3};
  160. flags.setPathEffect(SkDashPathEffect::Make(
  161. intervals, sizeof(intervals) / sizeof(intervals[0]), /*phase=*/0));
  162. canvas->DrawPath(dotted_path, flags);
  163. }
  164. void ReferenceLines::SetTopLabel(float top) {
  165. top_ = top;
  166. right_top_label_->SetText(GenerateLabelText(top_, y_unit_));
  167. right_middle_label_->SetText(
  168. GenerateLabelText((top_ - bottom_) / 2, y_unit_));
  169. // This might trigger label resize.
  170. InvalidateLayout();
  171. }
  172. void ReferenceLines::SetBottomLabel(float bottom) {
  173. bottom_ = bottom;
  174. right_bottom_label_->SetText(GenerateLabelText(bottom_, y_unit_));
  175. right_middle_label_->SetText(
  176. GenerateLabelText((top_ - bottom_) / 2, y_unit_));
  177. // This might trigger label resize.
  178. InvalidateLayout();
  179. }
  180. void ReferenceLines::SetLeftLabel(float left) {
  181. left_ = left;
  182. left_bottom_label_->SetText(GenerateLabelText(left_, x_unit_));
  183. // This might trigger label resize.
  184. InvalidateLayout();
  185. }
  186. void ReferenceLines::SetVerticalTicksInterval(float interval) {
  187. interval == std::abs(interval) >= 1 ? 0 : std::abs(interval);
  188. if (interval == vertical_ticks_interval_)
  189. return;
  190. vertical_ticks_interval_ = interval;
  191. }
  192. } // namespace hud_display
  193. } // namespace ash