mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 20:52:45 +00:00 
			
		
		
		
	PixelPaint: Tool properties panel
Each tool can have its own set of properties that can be modified through a panel on the right side. The tools I've added properties for are: Pen: Thickness Brush: Size Hardness Spray: Thickness Density Bucket: Threshold
This commit is contained in:
		
							parent
							
								
									544f2f3c96
								
							
						
					
					
						commit
						afd52e2576
					
				
					 13 changed files with 324 additions and 12 deletions
				
			
		|  | @ -28,9 +28,13 @@ | ||||||
| #include "ImageEditor.h" | #include "ImageEditor.h" | ||||||
| #include "Layer.h" | #include "Layer.h" | ||||||
| #include <LibGUI/Action.h> | #include <LibGUI/Action.h> | ||||||
|  | #include <LibGUI/BoxLayout.h> | ||||||
|  | #include <LibGUI/Label.h> | ||||||
| #include <LibGUI/Painter.h> | #include <LibGUI/Painter.h> | ||||||
|  | #include <LibGUI/Slider.h> | ||||||
| #include <LibGfx/Color.h> | #include <LibGfx/Color.h> | ||||||
| #include <LibGfx/Rect.h> | #include <LibGfx/Rect.h> | ||||||
|  | #include <utility> | ||||||
| 
 | 
 | ||||||
| namespace PixelPaint { | namespace PixelPaint { | ||||||
| 
 | 
 | ||||||
|  | @ -70,7 +74,7 @@ void BrushTool::draw_point(Gfx::Bitmap& bitmap, const Gfx::Color& color, const G | ||||||
|             if (distance >= m_size) |             if (distance >= m_size) | ||||||
|                 continue; |                 continue; | ||||||
| 
 | 
 | ||||||
|             auto falloff = (1.0 - (distance / (float)m_size)) * 0.2; |             auto falloff = (1.0 - (distance / (float)m_size)) * (1.0f / (100 - m_hardness)); | ||||||
|             auto pixel_color = color; |             auto pixel_color = color; | ||||||
|             pixel_color.set_alpha(falloff * 255); |             pixel_color.set_alpha(falloff * 255); | ||||||
|             bitmap.set_pixel(x, y, bitmap.get_pixel(x, y).blend(pixel_color)); |             bitmap.set_pixel(x, y, bitmap.get_pixel(x, y).blend(pixel_color)); | ||||||
|  | @ -111,4 +115,52 @@ void BrushTool::draw_line(Gfx::Bitmap& bitmap, const Gfx::Color& color, const Gf | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | GUI::Widget* BrushTool::get_properties_widget() | ||||||
|  | { | ||||||
|  |     if (!m_properties_widget) { | ||||||
|  |         m_properties_widget = GUI::Widget::construct(); | ||||||
|  |         m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& size_container = m_properties_widget->add<GUI::Widget>(); | ||||||
|  |         size_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         size_container.set_preferred_size(0, 20); | ||||||
|  |         size_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& size_label = size_container.add<GUI::Label>("Size:"); | ||||||
|  |         size_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||||
|  |         size_label.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); | ||||||
|  |         size_label.set_preferred_size(80, 20); | ||||||
|  | 
 | ||||||
|  |         auto& size_slider = size_container.add<GUI::HorizontalSlider>(); | ||||||
|  |         size_slider.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         size_slider.set_preferred_size(0, 20); | ||||||
|  |         size_slider.set_range(1, 100); | ||||||
|  |         size_slider.set_value(m_size); | ||||||
|  |         size_slider.on_value_changed = [this](int value) { | ||||||
|  |             m_size = value; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         auto& hardness_container = m_properties_widget->add<GUI::Widget>(); | ||||||
|  |         hardness_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         hardness_container.set_preferred_size(0, 20); | ||||||
|  |         hardness_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& hardness_label = hardness_container.add<GUI::Label>("Hardness:"); | ||||||
|  |         hardness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||||
|  |         hardness_label.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); | ||||||
|  |         hardness_label.set_preferred_size(80, 20); | ||||||
|  | 
 | ||||||
|  |         auto& hardness_slider = hardness_container.add<GUI::HorizontalSlider>(); | ||||||
|  |         hardness_slider.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         hardness_slider.set_preferred_size(0, 20); | ||||||
|  |         hardness_slider.set_range(1, 99); | ||||||
|  |         hardness_slider.set_value(m_hardness); | ||||||
|  |         hardness_slider.on_value_changed = [this](int value) { | ||||||
|  |             m_hardness = value; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return m_properties_widget.ptr(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -37,9 +37,12 @@ public: | ||||||
| 
 | 
 | ||||||
|     virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; |     virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; | ||||||
|     virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; |     virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; | ||||||
|  |     virtual GUI::Widget* get_properties_widget() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     int m_size { 10 }; |     RefPtr<GUI::Widget> m_properties_widget; | ||||||
|  |     int m_size { 20 }; | ||||||
|  |     int m_hardness { 80 }; | ||||||
|     Gfx::IntPoint m_last_position; |     Gfx::IntPoint m_last_position; | ||||||
| 
 | 
 | ||||||
|     virtual const char* class_name() const override { return "BrushTool"; } |     virtual const char* class_name() const override { return "BrushTool"; } | ||||||
|  |  | ||||||
|  | @ -28,7 +28,10 @@ | ||||||
| #include "ImageEditor.h" | #include "ImageEditor.h" | ||||||
| #include "Layer.h" | #include "Layer.h" | ||||||
| #include <AK/Queue.h> | #include <AK/Queue.h> | ||||||
|  | #include <LibGUI/BoxLayout.h> | ||||||
|  | #include <LibGUI/Label.h> | ||||||
| #include <LibGUI/Painter.h> | #include <LibGUI/Painter.h> | ||||||
|  | #include <LibGUI/Slider.h> | ||||||
| #include <LibGfx/Bitmap.h> | #include <LibGfx/Bitmap.h> | ||||||
| #include <LibGfx/Rect.h> | #include <LibGfx/Rect.h> | ||||||
| 
 | 
 | ||||||
|  | @ -42,7 +45,15 @@ BucketTool::~BucketTool() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void flood_fill(Gfx::Bitmap& bitmap, const Gfx::IntPoint& start_position, Color target_color, Color fill_color) | static float color_distance_squared(const Gfx::Color& lhs, const Gfx::Color& rhs) | ||||||
|  | { | ||||||
|  |     int a = rhs.red() - lhs.red(); | ||||||
|  |     int b = rhs.green() - lhs.green(); | ||||||
|  |     int c = rhs.blue() - lhs.blue(); | ||||||
|  |     return (a * a + b * b + c * c) / (255.0f * 255.0f); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void flood_fill(Gfx::Bitmap& bitmap, const Gfx::IntPoint& start_position, Color target_color, Color fill_color, int threshold) | ||||||
| { | { | ||||||
|     ASSERT(bitmap.bpp() == 32); |     ASSERT(bitmap.bpp() == 32); | ||||||
| 
 | 
 | ||||||
|  | @ -52,12 +63,15 @@ static void flood_fill(Gfx::Bitmap& bitmap, const Gfx::IntPoint& start_position, | ||||||
|     if (!bitmap.rect().contains(start_position)) |     if (!bitmap.rect().contains(start_position)) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|  |     float threshold_normalized_squared = (threshold / 100.0f) * (threshold / 100.0f); | ||||||
|  | 
 | ||||||
|     Queue<Gfx::IntPoint> queue; |     Queue<Gfx::IntPoint> queue; | ||||||
|     queue.enqueue(start_position); |     queue.enqueue(start_position); | ||||||
|     while (!queue.is_empty()) { |     while (!queue.is_empty()) { | ||||||
|         auto position = queue.dequeue(); |         auto position = queue.dequeue(); | ||||||
| 
 | 
 | ||||||
|         if (bitmap.get_pixel<Gfx::StorageFormat::RGBA32>(position.x(), position.y()) != target_color) |         auto pixel_color = bitmap.get_pixel<Gfx::StorageFormat::RGBA32>(position.x(), position.y()); | ||||||
|  |         if (color_distance_squared(pixel_color, target_color) > threshold_normalized_squared) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
|         bitmap.set_pixel<Gfx::StorageFormat::RGBA32>(position.x(), position.y(), fill_color); |         bitmap.set_pixel<Gfx::StorageFormat::RGBA32>(position.x(), position.y(), fill_color); | ||||||
|  | @ -84,9 +98,38 @@ void BucketTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEv | ||||||
|     GUI::Painter painter(layer.bitmap()); |     GUI::Painter painter(layer.bitmap()); | ||||||
|     auto target_color = layer.bitmap().get_pixel(event.x(), event.y()); |     auto target_color = layer.bitmap().get_pixel(event.x(), event.y()); | ||||||
| 
 | 
 | ||||||
|     flood_fill(layer.bitmap(), event.position(), target_color, m_editor->color_for(event)); |     flood_fill(layer.bitmap(), event.position(), target_color, m_editor->color_for(event), m_threshold); | ||||||
| 
 | 
 | ||||||
|     layer.did_modify_bitmap(*m_editor->image()); |     layer.did_modify_bitmap(*m_editor->image()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | GUI::Widget* BucketTool::get_properties_widget() | ||||||
|  | { | ||||||
|  |     if (!m_properties_widget) { | ||||||
|  |         m_properties_widget = GUI::Widget::construct(); | ||||||
|  |         m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& threshold_container = m_properties_widget->add<GUI::Widget>(); | ||||||
|  |         threshold_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         threshold_container.set_preferred_size(0, 20); | ||||||
|  |         threshold_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& threshold_label = threshold_container.add<GUI::Label>("Threshold:"); | ||||||
|  |         threshold_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||||
|  |         threshold_label.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); | ||||||
|  |         threshold_label.set_preferred_size(80, 20); | ||||||
|  | 
 | ||||||
|  |         auto& threshold_slider = threshold_container.add<GUI::HorizontalSlider>(); | ||||||
|  |         threshold_slider.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         threshold_slider.set_preferred_size(0, 20); | ||||||
|  |         threshold_slider.set_range(0, 100); | ||||||
|  |         threshold_slider.set_value(m_threshold); | ||||||
|  |         threshold_slider.on_value_changed = [this](int value) { | ||||||
|  |             m_threshold = value; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return m_properties_widget.ptr(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -36,9 +36,13 @@ public: | ||||||
|     virtual ~BucketTool() override; |     virtual ~BucketTool() override; | ||||||
| 
 | 
 | ||||||
|     virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; |     virtual void on_mousedown(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; | ||||||
|  |     virtual GUI::Widget* get_properties_widget() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     virtual const char* class_name() const override { return "BucketTool"; } |     virtual const char* class_name() const override { return "BucketTool"; } | ||||||
|  | 
 | ||||||
|  |     RefPtr<GUI::Widget> m_properties_widget; | ||||||
|  |     int m_threshold { 0 }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,6 +18,7 @@ set(SOURCES | ||||||
|     RectangleTool.cpp |     RectangleTool.cpp | ||||||
|     SprayTool.cpp |     SprayTool.cpp | ||||||
|     ToolboxWidget.cpp |     ToolboxWidget.cpp | ||||||
|  |     ToolPropertiesWidget.cpp | ||||||
|     Tool.cpp |     Tool.cpp | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -28,8 +28,11 @@ | ||||||
| #include "ImageEditor.h" | #include "ImageEditor.h" | ||||||
| #include "Layer.h" | #include "Layer.h" | ||||||
| #include <LibGUI/Action.h> | #include <LibGUI/Action.h> | ||||||
|  | #include <LibGUI/BoxLayout.h> | ||||||
|  | #include <LibGUI/Label.h> | ||||||
| #include <LibGUI/Menu.h> | #include <LibGUI/Menu.h> | ||||||
| #include <LibGUI/Painter.h> | #include <LibGUI/Painter.h> | ||||||
|  | #include <LibGUI/Slider.h> | ||||||
| 
 | 
 | ||||||
| namespace PixelPaint { | namespace PixelPaint { | ||||||
| 
 | 
 | ||||||
|  | @ -94,4 +97,33 @@ void PenTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) | ||||||
|     m_context_menu->popup(event.screen_position()); |     m_context_menu->popup(event.screen_position()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | GUI::Widget* PenTool::get_properties_widget() | ||||||
|  | { | ||||||
|  |     if (!m_properties_widget) { | ||||||
|  |         m_properties_widget = GUI::Widget::construct(); | ||||||
|  |         m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& thickness_container = m_properties_widget->add<GUI::Widget>(); | ||||||
|  |         thickness_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         thickness_container.set_preferred_size(0, 20); | ||||||
|  |         thickness_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& thickness_label = thickness_container.add<GUI::Label>("Thickness:"); | ||||||
|  |         thickness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||||
|  |         thickness_label.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); | ||||||
|  |         thickness_label.set_preferred_size(80, 20); | ||||||
|  | 
 | ||||||
|  |         auto& thickness_slider = thickness_container.add<GUI::HorizontalSlider>(); | ||||||
|  |         thickness_slider.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         thickness_slider.set_preferred_size(0, 20); | ||||||
|  |         thickness_slider.set_range(1, 20); | ||||||
|  |         thickness_slider.set_value(m_thickness); | ||||||
|  |         thickness_slider.on_value_changed = [this](int value) { | ||||||
|  |             m_thickness = value; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return m_properties_widget.ptr(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -41,12 +41,14 @@ public: | ||||||
|     virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; |     virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; | ||||||
|     virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; |     virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; | ||||||
|     virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; |     virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; | ||||||
|  |     virtual GUI::Widget* get_properties_widget() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     virtual const char* class_name() const override { return "PenTool"; } |     virtual const char* class_name() const override { return "PenTool"; } | ||||||
| 
 | 
 | ||||||
|     Gfx::IntPoint m_last_drawing_event_position { -1, -1 }; |     Gfx::IntPoint m_last_drawing_event_position { -1, -1 }; | ||||||
|     RefPtr<GUI::Menu> m_context_menu; |     RefPtr<GUI::Menu> m_context_menu; | ||||||
|  |     RefPtr<GUI::Widget> m_properties_widget; | ||||||
|     int m_thickness { 1 }; |     int m_thickness { 1 }; | ||||||
|     GUI::ActionGroup m_thickness_actions; |     GUI::ActionGroup m_thickness_actions; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -29,8 +29,11 @@ | ||||||
| #include "Layer.h" | #include "Layer.h" | ||||||
| #include <AK/Queue.h> | #include <AK/Queue.h> | ||||||
| #include <LibGUI/Action.h> | #include <LibGUI/Action.h> | ||||||
|  | #include <LibGUI/BoxLayout.h> | ||||||
|  | #include <LibGUI/Label.h> | ||||||
| #include <LibGUI/Menu.h> | #include <LibGUI/Menu.h> | ||||||
| #include <LibGUI/Painter.h> | #include <LibGUI/Painter.h> | ||||||
|  | #include <LibGUI/Slider.h> | ||||||
| #include <LibGfx/Bitmap.h> | #include <LibGfx/Bitmap.h> | ||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  | @ -65,9 +68,9 @@ void SprayTool::paint_it() | ||||||
|     GUI::Painter painter(bitmap); |     GUI::Painter painter(bitmap); | ||||||
|     ASSERT(bitmap.bpp() == 32); |     ASSERT(bitmap.bpp() == 32); | ||||||
|     m_editor->update(); |     m_editor->update(); | ||||||
|     const double minimal_radius = 10; |     const double minimal_radius = 2; | ||||||
|     const double base_radius = minimal_radius * m_thickness; |     const double base_radius = minimal_radius * m_thickness; | ||||||
|     for (int i = 0; i < 100 + (nrand() * 800); i++) { |     for (int i = 0; i < M_PI * base_radius * base_radius * (m_density / 100.0f); i++) { | ||||||
|         double radius = base_radius * nrand(); |         double radius = base_radius * nrand(); | ||||||
|         double angle = 2 * M_PI * nrand(); |         double angle = 2 * M_PI * nrand(); | ||||||
|         const int xpos = m_last_pos.x() + radius * cos(angle); |         const int xpos = m_last_pos.x() + radius * cos(angle); | ||||||
|  | @ -125,4 +128,52 @@ void SprayTool::on_tool_button_contextmenu(GUI::ContextMenuEvent& event) | ||||||
|     m_context_menu->popup(event.screen_position()); |     m_context_menu->popup(event.screen_position()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | GUI::Widget* SprayTool::get_properties_widget() | ||||||
|  | { | ||||||
|  |     if (!m_properties_widget) { | ||||||
|  |         m_properties_widget = GUI::Widget::construct(); | ||||||
|  |         m_properties_widget->set_layout<GUI::VerticalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& thickness_container = m_properties_widget->add<GUI::Widget>(); | ||||||
|  |         thickness_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         thickness_container.set_preferred_size(0, 20); | ||||||
|  |         thickness_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& thickness_label = thickness_container.add<GUI::Label>("Thickness:"); | ||||||
|  |         thickness_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||||
|  |         thickness_label.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); | ||||||
|  |         thickness_label.set_preferred_size(80, 20); | ||||||
|  | 
 | ||||||
|  |         auto& thickness_slider = thickness_container.add<GUI::HorizontalSlider>(); | ||||||
|  |         thickness_slider.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         thickness_slider.set_preferred_size(0, 20); | ||||||
|  |         thickness_slider.set_range(1, 20); | ||||||
|  |         thickness_slider.set_value(m_thickness); | ||||||
|  |         thickness_slider.on_value_changed = [this](int value) { | ||||||
|  |             m_thickness = value; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         auto& density_container = m_properties_widget->add<GUI::Widget>(); | ||||||
|  |         density_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         density_container.set_preferred_size(0, 20); | ||||||
|  |         density_container.set_layout<GUI::HorizontalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |         auto& density_label = density_container.add<GUI::Label>("Density:"); | ||||||
|  |         density_label.set_text_alignment(Gfx::TextAlignment::CenterLeft); | ||||||
|  |         density_label.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); | ||||||
|  |         density_label.set_preferred_size(80, 20); | ||||||
|  | 
 | ||||||
|  |         auto& density_slider = density_container.add<GUI::HorizontalSlider>(); | ||||||
|  |         density_slider.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); | ||||||
|  |         density_slider.set_preferred_size(0, 30); | ||||||
|  |         density_slider.set_range(1, 100); | ||||||
|  |         density_slider.set_value(m_density); | ||||||
|  |         density_slider.on_value_changed = [this](int value) { | ||||||
|  |             m_density = value; | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return m_properties_widget.ptr(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -42,16 +42,20 @@ public: | ||||||
|     virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; |     virtual void on_mouseup(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; | ||||||
|     virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; |     virtual void on_mousemove(Layer&, GUI::MouseEvent& layer_event, GUI::MouseEvent& image_event) override; | ||||||
|     virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; |     virtual void on_tool_button_contextmenu(GUI::ContextMenuEvent&) override; | ||||||
|  |     virtual GUI::Widget* get_properties_widget() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     virtual const char* class_name() const override { return "SprayTool"; } |     virtual const char* class_name() const override { return "SprayTool"; } | ||||||
|     void paint_it(); |     void paint_it(); | ||||||
|  | 
 | ||||||
|  |     RefPtr<GUI::Widget> m_properties_widget; | ||||||
|     RefPtr<Core::Timer> m_timer; |     RefPtr<Core::Timer> m_timer; | ||||||
|     Gfx::IntPoint m_last_pos; |     Gfx::IntPoint m_last_pos; | ||||||
|     Color m_color; |     Color m_color; | ||||||
|     RefPtr<GUI::Menu> m_context_menu; |     RefPtr<GUI::Menu> m_context_menu; | ||||||
|     GUI::ActionGroup m_thickness_actions; |     GUI::ActionGroup m_thickness_actions; | ||||||
|     int m_thickness { 1 }; |     int m_thickness { 10 }; | ||||||
|  |     int m_density { 40 }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ public: | ||||||
|     virtual void on_second_paint(const Layer&, GUI::PaintEvent&) { } |     virtual void on_second_paint(const Layer&, GUI::PaintEvent&) { } | ||||||
|     virtual void on_keydown(GUI::KeyEvent&) { } |     virtual void on_keydown(GUI::KeyEvent&) { } | ||||||
|     virtual void on_keyup(GUI::KeyEvent&) { } |     virtual void on_keyup(GUI::KeyEvent&) { } | ||||||
|  |     virtual GUI::Widget* get_properties_widget() { return nullptr; } | ||||||
| 
 | 
 | ||||||
|     virtual bool is_move_tool() const { return false; } |     virtual bool is_move_tool() const { return false; } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										61
									
								
								Applications/PixelPaint/ToolPropertiesWidget.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								Applications/PixelPaint/ToolPropertiesWidget.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "ToolPropertiesWidget.h" | ||||||
|  | #include "Tool.h" | ||||||
|  | #include <LibGUI/BoxLayout.h> | ||||||
|  | #include <LibGUI/GroupBox.h> | ||||||
|  | 
 | ||||||
|  | namespace PixelPaint { | ||||||
|  | 
 | ||||||
|  | ToolPropertiesWidget::ToolPropertiesWidget() | ||||||
|  | { | ||||||
|  |     set_layout<GUI::VerticalBoxLayout>(); | ||||||
|  | 
 | ||||||
|  |     m_group_box = add<GUI::GroupBox>("Tool properties"); | ||||||
|  |     auto& layout = m_group_box->set_layout<GUI::VerticalBoxLayout>(); | ||||||
|  |     layout.set_margins({ 10, 20, 10, 10 }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ToolPropertiesWidget::set_active_tool(Tool* tool) | ||||||
|  | { | ||||||
|  |     if (tool == m_active_tool) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if (m_active_tool_widget != nullptr) | ||||||
|  |         m_group_box->remove_child(*m_active_tool_widget); | ||||||
|  | 
 | ||||||
|  |     m_active_tool = tool; | ||||||
|  |     m_active_tool_widget = tool->get_properties_widget(); | ||||||
|  |     if (m_active_tool_widget != nullptr) | ||||||
|  |         m_group_box->add_child(*m_active_tool_widget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ToolPropertiesWidget::~ToolPropertiesWidget() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								Applications/PixelPaint/ToolPropertiesWidget.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Applications/PixelPaint/ToolPropertiesWidget.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Ben Jilks <benjyjilks@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/RefPtr.h> | ||||||
|  | #include <LibGUI/Forward.h> | ||||||
|  | #include <LibGUI/Widget.h> | ||||||
|  | 
 | ||||||
|  | namespace PixelPaint { | ||||||
|  | 
 | ||||||
|  | class Tool; | ||||||
|  | 
 | ||||||
|  | class ToolPropertiesWidget final : public GUI::Widget { | ||||||
|  |     C_OBJECT(ToolPropertiesWidget); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     virtual ~ToolPropertiesWidget() override; | ||||||
|  | 
 | ||||||
|  |     void set_active_tool(Tool*); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     ToolPropertiesWidget(); | ||||||
|  | 
 | ||||||
|  |     RefPtr<GUI::GroupBox> m_group_box; | ||||||
|  | 
 | ||||||
|  |     Tool* m_active_tool { nullptr }; | ||||||
|  |     GUI::Widget* m_active_tool_widget { nullptr }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -33,6 +33,7 @@ | ||||||
| #include "LayerPropertiesWidget.h" | #include "LayerPropertiesWidget.h" | ||||||
| #include "PaletteWidget.h" | #include "PaletteWidget.h" | ||||||
| #include "Tool.h" | #include "Tool.h" | ||||||
|  | #include "ToolPropertiesWidget.h" | ||||||
| #include "ToolboxWidget.h" | #include "ToolboxWidget.h" | ||||||
| #include <LibGUI/AboutDialog.h> | #include <LibGUI/AboutDialog.h> | ||||||
| #include <LibGUI/Action.h> | #include <LibGUI/Action.h> | ||||||
|  | @ -84,10 +85,6 @@ int main(int argc, char** argv) | ||||||
|     auto& image_editor = vertical_container.add<PixelPaint::ImageEditor>(); |     auto& image_editor = vertical_container.add<PixelPaint::ImageEditor>(); | ||||||
|     image_editor.set_focus(true); |     image_editor.set_focus(true); | ||||||
| 
 | 
 | ||||||
|     toolbox.on_tool_selection = [&](auto* tool) { |  | ||||||
|         image_editor.set_active_tool(tool); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     vertical_container.add<PixelPaint::PaletteWidget>(image_editor); |     vertical_container.add<PixelPaint::PaletteWidget>(image_editor); | ||||||
| 
 | 
 | ||||||
|     auto& right_panel = horizontal_container.add<GUI::Widget>(); |     auto& right_panel = horizontal_container.add<GUI::Widget>(); | ||||||
|  | @ -100,6 +97,13 @@ int main(int argc, char** argv) | ||||||
| 
 | 
 | ||||||
|     auto& layer_properties_widget = right_panel.add<PixelPaint::LayerPropertiesWidget>(); |     auto& layer_properties_widget = right_panel.add<PixelPaint::LayerPropertiesWidget>(); | ||||||
| 
 | 
 | ||||||
|  |     auto& tool_properties_widget = right_panel.add<PixelPaint::ToolPropertiesWidget>(); | ||||||
|  | 
 | ||||||
|  |     toolbox.on_tool_selection = [&](auto* tool) { | ||||||
|  |         image_editor.set_active_tool(tool); | ||||||
|  |         tool_properties_widget.set_active_tool(tool); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     window->show(); |     window->show(); | ||||||
| 
 | 
 | ||||||
|     auto menubar = GUI::MenuBar::construct(); |     auto menubar = GUI::MenuBar::construct(); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 BenJilks
						BenJilks