Serenity Operating System
at master 788 lines 23 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibGUI/BoxLayout.h> 9#include <LibGUI/Button.h> 10#include <LibGUI/ColorPicker.h> 11#include <LibGUI/ConnectionToWindowServer.h> 12#include <LibGUI/Frame.h> 13#include <LibGUI/Label.h> 14#include <LibGUI/OpacitySlider.h> 15#include <LibGUI/Painter.h> 16#include <LibGUI/SpinBox.h> 17#include <LibGUI/TabWidget.h> 18#include <LibGUI/TextBox.h> 19#include <LibGfx/Palette.h> 20 21namespace GUI { 22 23class ColorButton : public AbstractButton { 24 C_OBJECT(ColorButton); 25 26public: 27 virtual ~ColorButton() override = default; 28 29 void set_selected(bool selected); 30 Color color() const { return m_color; } 31 32 Function<void(const Color)> on_click; 33 34protected: 35 virtual void click(unsigned modifiers = 0) override; 36 virtual void doubleclick_event(GUI::MouseEvent&) override; 37 virtual void paint_event(PaintEvent&) override; 38 39private: 40 explicit ColorButton(ColorPicker& picker, Color color = {}); 41 42 ColorPicker& m_picker; 43 Color m_color; 44 bool m_selected { false }; 45}; 46 47class ColorField final : public GUI::Frame { 48 C_OBJECT(ColorField); 49 50public: 51 Function<void(Color)> on_pick; 52 void set_color(Color); 53 void set_hue(double); 54 void set_hue_from_pick(double); 55 56private: 57 ColorField(Color color); 58 59 Color m_color; 60 // save hue separately so full white color doesn't reset it to 0 61 double m_hue; 62 63 RefPtr<Gfx::Bitmap> m_color_bitmap; 64 bool m_being_pressed { false }; 65 Gfx::IntPoint m_last_position; 66 67 void create_color_bitmap(); 68 void pick_color_at_position(GUI::MouseEvent& event); 69 void recalculate_position(); 70 71 virtual void mousedown_event(GUI::MouseEvent&) override; 72 virtual void mouseup_event(GUI::MouseEvent&) override; 73 virtual void mousemove_event(GUI::MouseEvent&) override; 74 virtual void paint_event(GUI::PaintEvent&) override; 75 virtual void resize_event(ResizeEvent&) override; 76}; 77 78class ColorSlider final : public GUI::Frame { 79 C_OBJECT(ColorSlider); 80 81public: 82 Function<void(double)> on_pick; 83 void set_value(double); 84 85private: 86 ColorSlider(double value); 87 88 double m_value; 89 90 RefPtr<Gfx::Bitmap> m_color_bitmap; 91 bool m_being_pressed { false }; 92 int m_last_position; 93 94 void pick_value_at_position(GUI::MouseEvent& event); 95 void recalculate_position(); 96 97 virtual void mousedown_event(GUI::MouseEvent&) override; 98 virtual void mouseup_event(GUI::MouseEvent&) override; 99 virtual void mousemove_event(GUI::MouseEvent&) override; 100 virtual void paint_event(GUI::PaintEvent&) override; 101 virtual void resize_event(ResizeEvent&) override; 102}; 103 104class ColorPreview final : public GUI::Widget { 105 C_OBJECT(ColorPreview); 106 107public: 108 void set_color(Color); 109 110private: 111 ColorPreview(Color); 112 113 Color m_color; 114 virtual void paint_event(GUI::PaintEvent&) override; 115}; 116 117class CustomColorWidget final : public GUI::Widget { 118 C_OBJECT(CustomColorWidget); 119 120public: 121 Function<void(Color)> on_pick; 122 void set_color(Color); 123 124private: 125 CustomColorWidget(Color); 126 127 RefPtr<ColorField> m_color_field; 128 RefPtr<ColorSlider> m_color_slider; 129}; 130 131class ColorSelectOverlay final : public Widget { 132 C_OBJECT(ColorSelectOverlay) 133public: 134 Optional<Color> exec() 135 { 136 m_event_loop = make<Core::EventLoop>(); 137 138 // FIXME: Allow creation of fully transparent windows without a backing store. 139 auto window = Window::construct(); 140 window->set_main_widget(this); 141 window->set_has_alpha_channel(true); 142 window->set_fullscreen(true); 143 window->set_frameless(true); 144 window->show(); 145 146 if (!m_event_loop->exec()) 147 return {}; 148 return m_col; 149 } 150 151 virtual ~ColorSelectOverlay() override = default; 152 Function<void(Color)> on_color_changed; 153 154private: 155 ColorSelectOverlay() 156 { 157 set_override_cursor(Gfx::StandardCursor::Eyedropper); 158 } 159 160 virtual void mousedown_event(GUI::MouseEvent&) override { m_event_loop->quit(1); } 161 virtual void mousemove_event(GUI::MouseEvent&) override 162 { 163 auto new_col = ConnectionToWindowServer::the().get_color_under_cursor(); 164 if (!new_col.has_value()) 165 return; 166 if (new_col == m_col) 167 return; 168 m_col = new_col.value(); 169 if (on_color_changed) 170 on_color_changed(m_col); 171 } 172 173 virtual void keydown_event(GUI::KeyEvent& event) override 174 { 175 if (event.key() == KeyCode::Key_Escape) { 176 event.accept(); 177 m_event_loop->quit(0); 178 return; 179 } 180 } 181 182 OwnPtr<Core::EventLoop> m_event_loop; 183 Color m_col; 184}; 185 186ColorPicker::ColorPicker(Color color, Window* parent_window, DeprecatedString title) 187 : Dialog(parent_window) 188 , m_color(color) 189{ 190 set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/color-chooser.png"sv).release_value_but_fixme_should_propagate_errors()); 191 set_title(title); 192 set_resizable(false); 193 resize(480, 326); 194 195 build_ui(); 196} 197 198void ColorPicker::set_color_has_alpha_channel(bool has_alpha) 199{ 200 if (m_color_has_alpha_channel == has_alpha) 201 return; 202 203 m_color_has_alpha_channel = has_alpha; 204 update_color_widgets(); 205} 206 207void ColorPicker::build_ui() 208{ 209 auto root_container = set_main_widget<Widget>().release_value_but_fixme_should_propagate_errors(); 210 root_container->set_layout<VerticalBoxLayout>(4); 211 root_container->set_fill_with_background_color(true); 212 213 auto& tab_widget = root_container->add<GUI::TabWidget>(); 214 215 auto& tab_palette = tab_widget.add_tab<Widget>("Palette"); 216 tab_palette.set_layout<VerticalBoxLayout>(4, 4); 217 218 build_ui_palette(tab_palette); 219 220 auto& tab_custom_color = tab_widget.add_tab<Widget>("Custom Color"); 221 tab_custom_color.set_layout<VerticalBoxLayout>(4, 4); 222 223 build_ui_custom(tab_custom_color); 224 225 auto& button_container = root_container->add<Widget>(); 226 button_container.set_preferred_height(GUI::SpecialDimension::Fit); 227 button_container.set_layout<HorizontalBoxLayout>(4); 228 button_container.add_spacer().release_value_but_fixme_should_propagate_errors(); 229 230 auto& ok_button = button_container.add<DialogButton>(); 231 ok_button.set_text("OK"_short_string); 232 ok_button.on_click = [this](auto) { 233 done(ExecResult::OK); 234 }; 235 ok_button.set_default(true); 236 237 auto& cancel_button = button_container.add<DialogButton>(); 238 cancel_button.set_text("Cancel"_short_string); 239 cancel_button.on_click = [this](auto) { 240 done(ExecResult::Cancel); 241 }; 242} 243 244void ColorPicker::build_ui_palette(Widget& root_container) 245{ 246 unsigned colors[4][9] = { 247 { 0xef2929, 0xf0b143, 0xfce94f, 0x9fe13a, 0x7c9ece, 0xa680a8, 0xe1ba70, 0x888a85, 0xeeeeec }, 248 { 0xba1e09, 0xf57900, 0xe9d51a, 0x8bd121, 0x4164a3, 0x6f517b, 0xb77f19, 0x555753, 0xd4d7cf }, 249 { 0x961605, 0xbf600c, 0xe9d51a, 0x619910, 0x2b4986, 0x573666, 0x875b09, 0x2f3436, 0xbbbdb6 }, 250 { 0x000000, 0x2f3436, 0x555753, 0x808080, 0xbabdb6, 0xd3d7cf, 0xeeeeec, 0xf3f3f3, 0xffffff } 251 }; 252 253 for (int r = 0; r < 4; r++) { 254 auto& colors_row = root_container.add<Widget>(); 255 colors_row.set_layout<HorizontalBoxLayout>(); 256 257 for (int i = 0; i < 8; i++) { 258 create_color_button(colors_row, colors[r][i]); 259 } 260 } 261} 262 263void ColorPicker::build_ui_custom(Widget& root_container) 264{ 265 enum RGBComponent { 266 Red, 267 Green, 268 Blue, 269 Alpha 270 }; 271 272 auto& horizontal_container = root_container.add<Widget>(); 273 horizontal_container.set_fill_with_background_color(true); 274 horizontal_container.set_layout<HorizontalBoxLayout>(); 275 276 // Left Side 277 m_custom_color = horizontal_container.add<CustomColorWidget>(m_color); 278 m_custom_color->set_preferred_size(299, 260); 279 m_custom_color->on_pick = [this](Color color) { 280 if (m_color == color) { 281 // NOTE: This call to update() is needed so that when changing the vertical color slider with the initial Color::White 282 // selected value (which doesn't change with that slider as in all the slider's values the new color at that position 283 // will still be Color::White) the spinbox colors are updated. 284 update(); 285 return; 286 } 287 288 m_alpha->set_base_color(color); 289 m_color = color; 290 update_color_widgets(); 291 }; 292 293 m_alpha = horizontal_container.add<GUI::VerticalOpacitySlider>(); 294 m_alpha->set_visible(m_color_has_alpha_channel); 295 m_alpha->set_min(0); 296 m_alpha->set_max(255); 297 m_alpha->set_value(m_color.alpha()); 298 m_alpha->on_change = [this](auto value) { 299 auto color = m_color; 300 color.set_alpha(value); 301 302 if (m_color == color) 303 return; 304 305 m_color = color; 306 m_custom_color->set_color(color); 307 update_color_widgets(); 308 }; 309 310 // Right Side 311 auto& vertical_container = horizontal_container.add<Widget>(); 312 vertical_container.set_layout<VerticalBoxLayout>(GUI::Margins { 0, 0, 0, 8 }); 313 vertical_container.set_min_width(120); 314 315 auto& preview_container = vertical_container.add<Frame>(); 316 preview_container.set_layout<VerticalBoxLayout>(2, 0); 317 preview_container.set_fixed_height(100); 318 319 // Current color 320 preview_container.add<ColorPreview>(m_color); 321 322 // Preview selected color 323 m_preview_widget = preview_container.add<ColorPreview>(m_color); 324 325 vertical_container.add_spacer().release_value_but_fixme_should_propagate_errors(); 326 327 // HTML 328 auto& html_container = vertical_container.add<GUI::Widget>(); 329 html_container.set_layout<GUI::HorizontalBoxLayout>(); 330 html_container.set_preferred_height(GUI::SpecialDimension::Fit); 331 332 auto& html_label = html_container.add<GUI::Label>(); 333 html_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); 334 html_label.set_preferred_width(48); 335 html_label.set_text("HTML:"); 336 337 m_html_text = html_container.add<GUI::TextBox>(); 338 m_html_text->set_text(m_color_has_alpha_channel ? m_color.to_deprecated_string() : m_color.to_deprecated_string_without_alpha()); 339 m_html_text->on_change = [this]() { 340 auto color_name = m_html_text->text(); 341 auto optional_color = Color::from_string(color_name); 342 if (optional_color.has_value() && (!color_name.starts_with('#') || color_name.length() == ((m_color_has_alpha_channel) ? 9 : 7))) { 343 // The color length must be 9/7 (unless it is a name like red), because: 344 // - If we allowed 5/4 character rgb color, the field would reset to 9/7 characters after you deleted 4/3 characters. 345 auto color = optional_color.value(); 346 if (m_color == color) 347 return; 348 349 m_color = optional_color.value(); 350 m_custom_color->set_color(color); 351 update_color_widgets(); 352 } 353 }; 354 355 // RGB Lines 356 auto make_spinbox = [&](RGBComponent component, int initial_value) { 357 auto& rgb_container = vertical_container.add<GUI::Widget>(); 358 rgb_container.set_layout<GUI::HorizontalBoxLayout>(); 359 rgb_container.set_preferred_height(GUI::SpecialDimension::Fit); 360 361 auto& rgb_label = rgb_container.add<GUI::Label>(); 362 rgb_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); 363 rgb_label.set_preferred_width(48); 364 365 auto& spinbox = rgb_container.add<SpinBox>(); 366 spinbox.set_min(0); 367 spinbox.set_max(255); 368 spinbox.set_value(initial_value); 369 spinbox.set_enabled(m_color_has_alpha_channel); 370 spinbox.on_change = [this, component](auto value) { 371 auto color = m_color; 372 373 if (component == Red) 374 color.set_red(value); 375 if (component == Green) 376 color.set_green(value); 377 if (component == Blue) 378 color.set_blue(value); 379 if (component == Alpha) 380 color.set_alpha(value); 381 382 if (m_color == color) 383 return; 384 385 m_color = color; 386 m_custom_color->set_color(color); 387 update_color_widgets(); 388 }; 389 390 if (component == Red) { 391 rgb_label.set_text("Red:"); 392 m_red_spinbox = spinbox; 393 } else if (component == Green) { 394 rgb_label.set_text("Green:"); 395 m_green_spinbox = spinbox; 396 } else if (component == Blue) { 397 rgb_label.set_text("Blue:"); 398 m_blue_spinbox = spinbox; 399 } else if (component == Alpha) { 400 rgb_label.set_text("Alpha:"); 401 m_alpha_spinbox = spinbox; 402 } 403 }; 404 405 make_spinbox(Red, m_color.red()); 406 make_spinbox(Green, m_color.green()); 407 make_spinbox(Blue, m_color.blue()); 408 make_spinbox(Alpha, m_color.alpha()); 409 410 m_selector_button = vertical_container.add<GUI::Button>("Select on screen"_string.release_value_but_fixme_should_propagate_errors()); 411 m_selector_button->on_click = [this](auto) { 412 auto selector = ColorSelectOverlay::construct(); 413 auto original_color = m_color; 414 // This allows us to use the color preview widget as a live-preview for 415 // the color currently under the cursor, which is helpful. 416 selector->on_color_changed = [this](auto color) { 417 m_color = color; 418 update_color_widgets(); 419 }; 420 421 // Set the final color 422 auto maybe_color = selector->exec(); 423 m_color = maybe_color.value_or(original_color); 424 m_custom_color->set_color(m_color); 425 update_color_widgets(); 426 }; 427} 428 429void ColorPicker::update_color_widgets() 430{ 431 m_preview_widget->set_color(m_color); 432 433 m_html_text->set_text(m_color_has_alpha_channel ? m_color.to_deprecated_string() : m_color.to_deprecated_string_without_alpha()); 434 435 m_red_spinbox->set_value(m_color.red()); 436 m_green_spinbox->set_value(m_color.green()); 437 m_blue_spinbox->set_value(m_color.blue()); 438 m_alpha_spinbox->set_value(m_color.alpha()); 439 m_alpha_spinbox->set_enabled(m_color_has_alpha_channel); 440 m_alpha->set_value(m_color.alpha()); 441 m_alpha->set_visible(m_color_has_alpha_channel); 442} 443 444void ColorPicker::create_color_button(Widget& container, unsigned rgb) 445{ 446 Color color = Color::from_rgb(rgb); 447 448 auto& widget = container.add<ColorButton>(*this, color); 449 widget.on_click = [this](Color color) { 450 for (auto& value : m_color_widgets) { 451 value.set_selected(false); 452 value.update(); 453 } 454 455 m_color = color; 456 m_custom_color->set_color(color); 457 update_color_widgets(); 458 }; 459 460 if (color == m_color) { 461 widget.set_selected(true); 462 } 463 464 m_color_widgets.append(widget); 465} 466 467ColorButton::ColorButton(ColorPicker& picker, Color color) 468 : m_picker(picker) 469{ 470 m_color = color; 471} 472 473void ColorButton::set_selected(bool selected) 474{ 475 m_selected = selected; 476} 477 478void ColorButton::doubleclick_event(GUI::MouseEvent&) 479{ 480 click(); 481 m_selected = true; 482 m_picker.done(Dialog::ExecResult::OK); 483} 484 485void ColorButton::paint_event(PaintEvent& event) 486{ 487 Painter painter(*this); 488 painter.add_clip_rect(event.rect()); 489 490 Gfx::StylePainter::paint_button(painter, rect(), palette(), Gfx::ButtonStyle::Normal, is_being_pressed(), is_hovered(), is_checked(), is_enabled(), is_focused()); 491 492 painter.fill_rect(rect().shrunken(2, 2), m_color); 493 494 if (m_selected) { 495 painter.fill_rect(rect().shrunken(6, 6), Color::Black); 496 painter.fill_rect(rect().shrunken(10, 10), Color::White); 497 painter.fill_rect(rect().shrunken(14, 14), m_color); 498 } 499} 500 501void ColorButton::click(unsigned) 502{ 503 if (on_click) 504 on_click(m_color); 505 506 m_selected = true; 507} 508 509CustomColorWidget::CustomColorWidget(Color color) 510{ 511 set_layout<HorizontalBoxLayout>(); 512 513 m_color_field = add<ColorField>(color); 514 auto size = 256 + (m_color_field->frame_thickness() * 2); 515 m_color_field->set_fixed_size(size, size); 516 m_color_field->on_pick = [this](Color color) { 517 if (on_pick) 518 on_pick(color); 519 }; 520 521 m_color_slider = add<ColorSlider>(color.to_hsv().hue); 522 auto slider_width = 24 + (m_color_slider->frame_thickness() * 2); 523 m_color_slider->set_fixed_size(slider_width, size); 524 m_color_slider->on_pick = [this](double value) { 525 m_color_field->set_hue_from_pick(value); 526 }; 527} 528 529void CustomColorWidget::set_color(Color color) 530{ 531 m_color_field->set_color(color); 532 m_color_field->set_hue(color.to_hsv().hue); 533 534 m_color_slider->set_value(color.to_hsv().hue); 535} 536 537ColorField::ColorField(Color color) 538 : m_color(color) 539 , m_hue(color.to_hsv().hue) 540{ 541 create_color_bitmap(); 542} 543 544void ColorField::create_color_bitmap() 545{ 546 m_color_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { 256, 256 }).release_value_but_fixme_should_propagate_errors(); 547 auto painter = Gfx::Painter(*m_color_bitmap); 548 549 Gfx::HSV hsv; 550 hsv.hue = m_hue; 551 for (int x = 0; x < 256; x++) { 552 hsv.saturation = x / 255.0; 553 for (int y = 0; y < 256; y++) { 554 hsv.value = (255 - y) / 255.0; 555 Color color = Color::from_hsv(hsv); 556 painter.set_pixel({ x, y }, color); 557 } 558 } 559} 560 561void ColorField::set_color(Color color) 562{ 563 if (m_color == color) 564 return; 565 566 m_color = color; 567 // don't save m_hue here by default, we don't want to set it to 0 in case color is full white 568 // m_hue = color.to_hsv().hue; 569 570 recalculate_position(); 571} 572 573void ColorField::recalculate_position() 574{ 575 Gfx::HSV hsv = m_color.to_hsv(); 576 auto x = hsv.saturation * width(); 577 auto y = (1 - hsv.value) * height(); 578 m_last_position = Gfx::IntPoint(x, y); 579 update(); 580} 581 582void ColorField::set_hue(double hue) 583{ 584 if (m_hue == hue) 585 return; 586 587 auto hsv = m_color.to_hsv(); 588 hsv.hue = hue; 589 590 m_hue = hue; 591 create_color_bitmap(); 592 593 auto color = Color::from_hsv(hsv); 594 color.set_alpha(m_color.alpha()); 595 set_color(color); 596} 597 598void ColorField::set_hue_from_pick(double hue) 599{ 600 set_hue(hue); 601 if (on_pick) 602 on_pick(m_color); 603} 604 605void ColorField::pick_color_at_position(GUI::MouseEvent& event) 606{ 607 if (!m_being_pressed) 608 return; 609 610 auto inner_rect = frame_inner_rect(); 611 auto position = event.position().constrained(inner_rect).translated(-frame_thickness(), -frame_thickness()); 612 auto color = Color::from_hsv(m_hue, (double)position.x() / inner_rect.width(), (double)(inner_rect.height() - position.y()) / inner_rect.height()); 613 color.set_alpha(m_color.alpha()); 614 m_last_position = position; 615 m_color = color; 616 617 if (on_pick) 618 on_pick(color); 619 620 update(); 621} 622 623void ColorField::mousedown_event(GUI::MouseEvent& event) 624{ 625 if (event.button() == GUI::MouseButton::Primary) { 626 m_being_pressed = true; 627 pick_color_at_position(event); 628 } 629} 630 631void ColorField::mouseup_event(GUI::MouseEvent& event) 632{ 633 if (event.button() == GUI::MouseButton::Primary) { 634 m_being_pressed = false; 635 pick_color_at_position(event); 636 } 637} 638 639void ColorField::mousemove_event(GUI::MouseEvent& event) 640{ 641 if (event.buttons() & GUI::MouseButton::Primary) 642 pick_color_at_position(event); 643} 644 645void ColorField::paint_event(GUI::PaintEvent& event) 646{ 647 Frame::paint_event(event); 648 649 Painter painter(*this); 650 painter.add_clip_rect(event.rect()); 651 painter.add_clip_rect(frame_inner_rect()); 652 653 painter.draw_scaled_bitmap(frame_inner_rect(), *m_color_bitmap, m_color_bitmap->rect()); 654 655 painter.translate(frame_thickness(), frame_thickness()); 656 painter.draw_line({ m_last_position.x() - 1, 0 }, { m_last_position.x() - 1, height() }, Color::White); 657 painter.draw_line({ m_last_position.x() + 1, 0 }, { m_last_position.x() + 1, height() }, Color::White); 658 painter.draw_line({ 0, m_last_position.y() - 1 }, { width(), m_last_position.y() - 1 }, Color::White); 659 painter.draw_line({ 0, m_last_position.y() + 1 }, { width(), m_last_position.y() + 1 }, Color::White); 660 painter.draw_line({ m_last_position.x(), 0 }, { m_last_position.x(), height() }, Color::Black); 661 painter.draw_line({ 0, m_last_position.y() }, { width(), m_last_position.y() }, Color::Black); 662} 663 664void ColorField::resize_event(ResizeEvent&) 665{ 666 recalculate_position(); 667} 668 669ColorSlider::ColorSlider(double value) 670 : m_value(value) 671{ 672 m_color_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, { 32, 360 }).release_value_but_fixme_should_propagate_errors(); 673 auto painter = Gfx::Painter(*m_color_bitmap); 674 675 for (int h = 0; h < 360; h++) { 676 Gfx::HSV hsv; 677 hsv.hue = h; 678 hsv.saturation = 1.0; 679 hsv.value = 1.0; 680 Color color = Color::from_hsv(hsv); 681 painter.draw_line({ 0, h }, { 32, h }, color); 682 } 683} 684 685void ColorSlider::set_value(double value) 686{ 687 if (m_value == value) 688 return; 689 690 m_value = value; 691 recalculate_position(); 692} 693 694void ColorSlider::recalculate_position() 695{ 696 m_last_position = (m_value / 360.0) * height(); 697 update(); 698} 699 700void ColorSlider::pick_value_at_position(GUI::MouseEvent& event) 701{ 702 if (!m_being_pressed) 703 return; 704 705 auto inner_rect = frame_inner_rect(); 706 auto position = event.position().constrained(inner_rect).translated(-frame_thickness(), -frame_thickness()); 707 auto hue = (double)position.y() / inner_rect.height() * 360; 708 m_last_position = position.y(); 709 m_value = hue; 710 711 if (on_pick) 712 on_pick(m_value); 713 714 update(); 715} 716 717void ColorSlider::mousedown_event(GUI::MouseEvent& event) 718{ 719 if (event.button() == GUI::MouseButton::Primary) { 720 m_being_pressed = true; 721 pick_value_at_position(event); 722 } 723} 724 725void ColorSlider::mouseup_event(GUI::MouseEvent& event) 726{ 727 if (event.button() == GUI::MouseButton::Primary) { 728 m_being_pressed = false; 729 pick_value_at_position(event); 730 } 731} 732 733void ColorSlider::mousemove_event(GUI::MouseEvent& event) 734{ 735 if (event.buttons() & GUI::MouseButton::Primary) 736 pick_value_at_position(event); 737} 738 739void ColorSlider::paint_event(GUI::PaintEvent& event) 740{ 741 Frame::paint_event(event); 742 743 Painter painter(*this); 744 painter.add_clip_rect(event.rect()); 745 painter.add_clip_rect(frame_inner_rect()); 746 747 painter.draw_scaled_bitmap(frame_inner_rect(), *m_color_bitmap, m_color_bitmap->rect()); 748 749 painter.translate(frame_thickness(), frame_thickness()); 750 painter.draw_line({ 0, m_last_position - 1 }, { width(), m_last_position - 1 }, Color::White); 751 painter.draw_line({ 0, m_last_position + 1 }, { width(), m_last_position + 1 }, Color::White); 752 painter.draw_line({ 0, m_last_position }, { width(), m_last_position }, Color::Black); 753} 754 755void ColorSlider::resize_event(ResizeEvent&) 756{ 757 recalculate_position(); 758} 759 760ColorPreview::ColorPreview(Color color) 761 : m_color(color) 762{ 763} 764 765void ColorPreview::set_color(Color color) 766{ 767 if (m_color == color) 768 return; 769 770 m_color = color; 771 update(); 772} 773 774void ColorPreview::paint_event(PaintEvent& event) 775{ 776 Painter painter(*this); 777 painter.add_clip_rect(event.rect()); 778 779 if (m_color.alpha() < 255) { 780 Gfx::StylePainter::paint_transparency_grid(painter, rect(), palette()); 781 painter.fill_rect(rect(), m_color); 782 painter.fill_rect({ 0, 0, rect().width() / 4, rect().height() }, m_color.with_alpha(255)); 783 } else { 784 painter.fill_rect(rect(), m_color); 785 } 786} 787 788}