OpacitySlider.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGUI/OpacitySlider.h>
  7. #include <LibGUI/Painter.h>
  8. #include <LibGfx/Palette.h>
  9. #include <LibGfx/StylePainter.h>
  10. REGISTER_WIDGET(GUI, OpacitySlider)
  11. namespace GUI {
  12. OpacitySlider::OpacitySlider(Gfx::Orientation orientation)
  13. : AbstractSlider(orientation)
  14. {
  15. // FIXME: Implement vertical mode.
  16. VERIFY(orientation == Gfx::Orientation::Horizontal);
  17. set_min(0);
  18. set_max(100);
  19. set_value(100);
  20. set_fixed_height(20);
  21. }
  22. OpacitySlider::~OpacitySlider()
  23. {
  24. }
  25. Gfx::IntRect OpacitySlider::frame_inner_rect() const
  26. {
  27. return rect().shrunken(4, 4);
  28. }
  29. void OpacitySlider::paint_event(PaintEvent& event)
  30. {
  31. GUI::Painter painter(*this);
  32. painter.add_clip_rect(event.rect());
  33. auto inner_rect = frame_inner_rect();
  34. // Grid pattern
  35. Gfx::StylePainter::paint_transparency_grid(painter, inner_rect, palette());
  36. // Alpha gradient
  37. for (int x = inner_rect.left(); x <= inner_rect.right(); ++x) {
  38. float relative_offset = (float)x / (float)width();
  39. float alpha = relative_offset * 255.0f;
  40. Color color { 0, 0, 0, (u8)alpha };
  41. painter.fill_rect({ x, inner_rect.y(), 1, inner_rect.height() }, color);
  42. }
  43. constexpr int notch_size = 3;
  44. int notch_y_top = inner_rect.top() + notch_size;
  45. int notch_y_bottom = inner_rect.bottom() - notch_size;
  46. int notch_x = inner_rect.left() + ((float)value() / (float)max() * (float)inner_rect.width());
  47. // Top notch
  48. painter.set_pixel(notch_x, notch_y_top, palette().threed_shadow2());
  49. for (int i = notch_size; i >= 0; --i) {
  50. painter.set_pixel(notch_x - (i + 1), notch_y_top - i - 1, palette().threed_highlight());
  51. for (int j = 0; j < i * 2; ++j) {
  52. painter.set_pixel(notch_x - (i + 1) + j + 1, notch_y_top - i - 1, palette().button());
  53. }
  54. painter.set_pixel(notch_x + (i + 0), notch_y_top - i - 1, palette().threed_shadow1());
  55. painter.set_pixel(notch_x + (i + 1), notch_y_top - i - 1, palette().threed_shadow2());
  56. }
  57. // Bottom notch
  58. painter.set_pixel(notch_x, notch_y_bottom, palette().threed_shadow2());
  59. for (int i = 0; i < notch_size; ++i) {
  60. painter.set_pixel(notch_x - (i + 1), notch_y_bottom + i + 1, palette().threed_highlight());
  61. for (int j = 0; j < i * 2; ++j) {
  62. painter.set_pixel(notch_x - (i + 1) + j + 1, notch_y_bottom + i + 1, palette().button());
  63. }
  64. painter.set_pixel(notch_x + (i + 0), notch_y_bottom + i + 1, palette().threed_shadow1());
  65. painter.set_pixel(notch_x + (i + 1), notch_y_bottom + i + 1, palette().threed_shadow2());
  66. }
  67. // Hairline
  68. // NOTE: If we're in the whiter part of the gradient, the notch is painted as shadow between the notches.
  69. // If we're in the darker part, the notch is painted as highlight.
  70. // We adjust the hairline's x position so it lines up with the shadow/highlight of the notches.
  71. u8 h = ((float)value() / (float)max()) * 255.0f;
  72. if (h < 128)
  73. painter.draw_line({ notch_x, notch_y_top }, { notch_x, notch_y_bottom }, Color(h, h, h, 255));
  74. else
  75. painter.draw_line({ notch_x - 1, notch_y_top }, { notch_x - 1, notch_y_bottom }, Color(h, h, h, 255));
  76. // Text label
  77. auto percent_text = String::formatted("{}%", (int)((float)value() / (float)max() * 100.0f));
  78. painter.draw_text(inner_rect.translated(1, 1), percent_text, Gfx::TextAlignment::Center, Color::Black);
  79. painter.draw_text(inner_rect, percent_text, Gfx::TextAlignment::Center, Color::White);
  80. // Frame
  81. Gfx::StylePainter::paint_frame(painter, rect(), palette(), Gfx::FrameShape::Container, Gfx::FrameShadow::Sunken, 2);
  82. }
  83. int OpacitySlider::value_at(const Gfx::IntPoint& position) const
  84. {
  85. auto inner_rect = frame_inner_rect();
  86. if (position.x() < inner_rect.left())
  87. return min();
  88. if (position.x() > inner_rect.right())
  89. return max();
  90. float relative_offset = (float)(position.x() - inner_rect.x()) / (float)inner_rect.width();
  91. return relative_offset * (float)max();
  92. }
  93. void OpacitySlider::mousedown_event(MouseEvent& event)
  94. {
  95. if (event.button() == MouseButton::Primary) {
  96. m_dragging = true;
  97. set_value(value_at(event.position()));
  98. return;
  99. }
  100. AbstractSlider::mousedown_event(event);
  101. }
  102. void OpacitySlider::mousemove_event(MouseEvent& event)
  103. {
  104. if (m_dragging) {
  105. set_value(value_at(event.position()));
  106. return;
  107. }
  108. AbstractSlider::mousemove_event(event);
  109. }
  110. void OpacitySlider::mouseup_event(MouseEvent& event)
  111. {
  112. if (event.button() == MouseButton::Primary) {
  113. m_dragging = false;
  114. return;
  115. }
  116. AbstractSlider::mouseup_event(event);
  117. }
  118. void OpacitySlider::mousewheel_event(MouseEvent& event)
  119. {
  120. set_value(value() - event.wheel_delta());
  121. }
  122. }