OpacitySlider.cpp 4.8 KB

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