diff --git a/Userland/Applications/PixelPaint/BrushTool.cpp b/Userland/Applications/PixelPaint/BrushTool.cpp index ce8088fd27..774a819781 100644 --- a/Userland/Applications/PixelPaint/BrushTool.cpp +++ b/Userland/Applications/PixelPaint/BrushTool.cpp @@ -39,7 +39,7 @@ void BrushTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEve return; draw_line(layer.bitmap(), m_editor->color_for(event), m_last_position, event.position()); - layer.did_modify_bitmap(*m_editor->image()); + layer.did_modify_bitmap(); m_last_position = event.position(); m_was_drawing = true; } diff --git a/Userland/Applications/PixelPaint/BucketTool.cpp b/Userland/Applications/PixelPaint/BucketTool.cpp index 2e49e257a1..412b6041b2 100644 --- a/Userland/Applications/PixelPaint/BucketTool.cpp +++ b/Userland/Applications/PixelPaint/BucketTool.cpp @@ -80,7 +80,7 @@ void BucketTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEv 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->did_complete_action(); } diff --git a/Userland/Applications/PixelPaint/EraseTool.cpp b/Userland/Applications/PixelPaint/EraseTool.cpp index 67da2de555..82f2ab0c87 100644 --- a/Userland/Applications/PixelPaint/EraseTool.cpp +++ b/Userland/Applications/PixelPaint/EraseTool.cpp @@ -39,7 +39,7 @@ void EraseTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEve Gfx::IntRect r = build_rect(event.position(), layer.rect()); GUI::Painter painter(layer.bitmap()); painter.clear_rect(r, get_color()); - layer.did_modify_bitmap(*m_editor->image()); + layer.did_modify_bitmap(); } void EraseTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent&) @@ -48,7 +48,7 @@ void EraseTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEve Gfx::IntRect r = build_rect(event.position(), layer.rect()); GUI::Painter painter(layer.bitmap()); painter.clear_rect(r, get_color()); - layer.did_modify_bitmap(*m_editor->image()); + layer.did_modify_bitmap(); } } diff --git a/Userland/Applications/PixelPaint/ImageEditor.cpp b/Userland/Applications/PixelPaint/ImageEditor.cpp index 96f4559a9b..7c042eb77c 100644 --- a/Userland/Applications/PixelPaint/ImageEditor.cpp +++ b/Userland/Applications/PixelPaint/ImageEditor.cpp @@ -14,51 +14,31 @@ #include #include -REGISTER_WIDGET(PixelPaint, ImageEditor); - namespace PixelPaint { -ImageEditor::ImageEditor() - : m_undo_stack(make()) +ImageEditor::ImageEditor(NonnullRefPtr image) + : m_image(move(image)) + , m_undo_stack(make()) , m_selection(*this) { set_focus_policy(GUI::FocusPolicy::StrongFocus); + m_undo_stack = make(); + m_undo_stack->push(make(*m_image)); + m_image->add_client(*this); } ImageEditor::~ImageEditor() { - if (m_image) - m_image->remove_client(*this); -} - -void ImageEditor::set_image(RefPtr image) -{ - if (m_image) - m_image->remove_client(*this); - - m_selection.clear(); - m_image = move(image); - m_active_layer = nullptr; - m_undo_stack = make(); - m_undo_stack->push(make(*m_image)); - update(); - relayout(); - - if (m_image) - m_image->add_client(*this); + m_image->remove_client(*this); } void ImageEditor::did_complete_action() { - if (!m_image) - return; m_undo_stack->push(make(*m_image)); } bool ImageEditor::undo() { - if (!m_image) - return false; if (m_undo_stack->can_undo()) { m_undo_stack->undo(); layers_did_change(); @@ -69,8 +49,6 @@ bool ImageEditor::undo() bool ImageEditor::redo() { - if (!m_image) - return false; if (m_undo_stack->can_redo()) { m_undo_stack->redo(); layers_did_change(); @@ -89,10 +67,8 @@ void ImageEditor::paint_event(GUI::PaintEvent& event) Gfx::StylePainter::paint_transparency_grid(painter, rect(), palette()); - if (m_image) { - painter.draw_rect(m_editor_image_rect.inflated(2, 2), Color::Black); - m_image->paint_into(painter, m_editor_image_rect); - } + painter.draw_rect(m_editor_image_rect.inflated(2, 2), Color::Black); + m_image->paint_into(painter, m_editor_image_rect); if (m_active_layer) { painter.draw_rect(enclosing_int_rect(image_rect_to_editor_rect(m_active_layer->relative_rect())).inflated(2, 2), Color::Black); @@ -271,6 +247,7 @@ void ImageEditor::set_active_layer(Layer* layer) m_active_layer = layer; if (m_active_layer) { + VERIFY(&m_active_layer->image() == m_image.ptr()); size_t index = 0; for (; index < m_image->layer_count(); ++index) { if (&m_image->layer(index) == layer) @@ -343,8 +320,6 @@ void ImageEditor::set_secondary_color(Color color) Layer* ImageEditor::layer_at_editor_position(Gfx::IntPoint const& editor_position) { - if (!m_image) - return nullptr; auto image_position = editor_position_to_image_position(editor_position); for (ssize_t i = m_image->layer_count() - 1; i >= 0; --i) { auto& layer = m_image->layer(i); diff --git a/Userland/Applications/PixelPaint/ImageEditor.h b/Userland/Applications/PixelPaint/ImageEditor.h index f57f040a64..28e9c5e97c 100644 --- a/Userland/Applications/PixelPaint/ImageEditor.h +++ b/Userland/Applications/PixelPaint/ImageEditor.h @@ -28,8 +28,6 @@ public: Image const* image() const { return m_image; } Image* image() { return m_image; } - void set_image(RefPtr); - Layer* active_layer() { return m_active_layer; } void set_active_layer(Layer*); @@ -73,7 +71,7 @@ public: Gfx::FloatPoint editor_position_to_image_position(Gfx::IntPoint const&) const; private: - ImageEditor(); + explicit ImageEditor(NonnullRefPtr); virtual void paint_event(GUI::PaintEvent&) override; virtual void second_paint_event(GUI::PaintEvent&) override; @@ -95,7 +93,7 @@ private: void clamped_scale(float); void relayout(); - RefPtr m_image; + NonnullRefPtr m_image; RefPtr m_active_layer; OwnPtr m_undo_stack; diff --git a/Userland/Applications/PixelPaint/Layer.cpp b/Userland/Applications/PixelPaint/Layer.cpp index a64324bb45..7aa59bcc92 100644 --- a/Userland/Applications/PixelPaint/Layer.cpp +++ b/Userland/Applications/PixelPaint/Layer.cpp @@ -61,9 +61,9 @@ Layer::Layer(Image& image, NonnullRefPtr bitmap, String name) { } -void Layer::did_modify_bitmap(Image& image) +void Layer::did_modify_bitmap() { - image.layer_did_modify_bitmap({}, *this); + m_image.layer_did_modify_bitmap({}, *this); } void Layer::set_visible(bool visible) diff --git a/Userland/Applications/PixelPaint/Layer.h b/Userland/Applications/PixelPaint/Layer.h index 3aa1a0056f..7991cffb94 100644 --- a/Userland/Applications/PixelPaint/Layer.h +++ b/Userland/Applications/PixelPaint/Layer.h @@ -46,7 +46,7 @@ public: void set_bitmap(NonnullRefPtr bitmap) { m_bitmap = move(bitmap); } - void did_modify_bitmap(Image&); + void did_modify_bitmap(); void set_selected(bool selected) { m_selected = selected; } bool is_selected() const { return m_selected; } @@ -59,6 +59,8 @@ public: RefPtr try_copy_bitmap(Selection const&) const; + Image const& image() const { return m_image; } + private: Layer(Image&, NonnullRefPtr, String name); diff --git a/Userland/Applications/PixelPaint/LineTool.cpp b/Userland/Applications/PixelPaint/LineTool.cpp index 7577f96e6b..af058e424f 100644 --- a/Userland/Applications/PixelPaint/LineTool.cpp +++ b/Userland/Applications/PixelPaint/LineTool.cpp @@ -57,7 +57,7 @@ void LineTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent& GUI::Painter painter(layer.bitmap()); painter.draw_line(m_line_start_position, m_line_end_position, m_editor->color_for(m_drawing_button), m_thickness); m_drawing_button = GUI::MouseButton::None; - layer.did_modify_bitmap(*m_editor->image()); + layer.did_modify_bitmap(); m_editor->did_complete_action(); } } diff --git a/Userland/Applications/PixelPaint/PenTool.cpp b/Userland/Applications/PixelPaint/PenTool.cpp index 1149f767d7..d26e24acef 100644 --- a/Userland/Applications/PixelPaint/PenTool.cpp +++ b/Userland/Applications/PixelPaint/PenTool.cpp @@ -31,7 +31,7 @@ void PenTool::on_mousedown(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent GUI::Painter painter(layer.bitmap()); painter.draw_line(event.position(), event.position(), m_editor->color_for(event), m_thickness); - layer.did_modify_bitmap(*m_editor->image()); + layer.did_modify_bitmap(); m_last_drawing_event_position = event.position(); } @@ -53,7 +53,7 @@ void PenTool::on_mousemove(Layer& layer, GUI::MouseEvent& event, GUI::MouseEvent painter.draw_line(m_last_drawing_event_position, event.position(), m_editor->color_for(event), m_thickness); else painter.draw_line(event.position(), event.position(), m_editor->color_for(event), m_thickness); - layer.did_modify_bitmap(*m_editor->image()); + layer.did_modify_bitmap(); m_last_drawing_event_position = event.position(); } diff --git a/Userland/Applications/PixelPaint/PixelPaintWindow.gml b/Userland/Applications/PixelPaint/PixelPaintWindow.gml index 7f978b1d48..568c36ce40 100644 --- a/Userland/Applications/PixelPaint/PixelPaintWindow.gml +++ b/Userland/Applications/PixelPaint/PixelPaintWindow.gml @@ -32,8 +32,8 @@ spacing: 0 } - @PixelPaint::ImageEditor { - name: "image_editor" + @GUI::TabWidget { + name: "tab_widget" } @PixelPaint::PaletteWidget { diff --git a/Userland/Applications/PixelPaint/RectangleTool.cpp b/Userland/Applications/PixelPaint/RectangleTool.cpp index bb14cc8ffd..d9ec04015d 100644 --- a/Userland/Applications/PixelPaint/RectangleTool.cpp +++ b/Userland/Applications/PixelPaint/RectangleTool.cpp @@ -61,7 +61,7 @@ void RectangleTool::on_mouseup(Layer& layer, GUI::MouseEvent& event, GUI::MouseE auto rect = Gfx::IntRect::from_two_points(m_rectangle_start_position, m_rectangle_end_position); draw_using(painter, rect); m_drawing_button = GUI::MouseButton::None; - layer.did_modify_bitmap(*m_editor->image()); + layer.did_modify_bitmap(); m_editor->did_complete_action(); } } diff --git a/Userland/Applications/PixelPaint/SprayTool.cpp b/Userland/Applications/PixelPaint/SprayTool.cpp index 2b40a93f9a..28aa9d9c23 100644 --- a/Userland/Applications/PixelPaint/SprayTool.cpp +++ b/Userland/Applications/PixelPaint/SprayTool.cpp @@ -62,7 +62,7 @@ void SprayTool::paint_it() bitmap.set_pixel(xpos, ypos, m_color); } - layer->did_modify_bitmap(*m_editor->image()); + layer->did_modify_bitmap(); } void SprayTool::on_mousedown(Layer&, GUI::MouseEvent& event, GUI::MouseEvent&) diff --git a/Userland/Applications/PixelPaint/Tool.h b/Userland/Applications/PixelPaint/Tool.h index b359dd0684..17ec76fb56 100644 --- a/Userland/Applications/PixelPaint/Tool.h +++ b/Userland/Applications/PixelPaint/Tool.h @@ -31,6 +31,9 @@ public: void clear() { m_editor = nullptr; } void setup(ImageEditor&); + ImageEditor const* editor() const { return m_editor; } + ImageEditor* editor() { return m_editor; } + GUI::Action* action() { return m_action; } void set_action(GUI::Action*); diff --git a/Userland/Applications/PixelPaint/main.cpp b/Userland/Applications/PixelPaint/main.cpp index 1aae299a72..d4761cefb8 100644 --- a/Userland/Applications/PixelPaint/main.cpp +++ b/Userland/Applications/PixelPaint/main.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -58,15 +59,23 @@ int main(int argc, char** argv) main_widget.load_from_gml(pixel_paint_window_gml); auto& toolbox = *main_widget.find_descendant_of_type_named("toolbox"); - auto& image_editor = *main_widget.find_descendant_of_type_named("image_editor"); - image_editor.set_focus(true); + auto& tab_widget = *main_widget.find_descendant_of_type_named("tab_widget"); + tab_widget.set_container_margins({ 4, 4, 5, 5 }); auto& palette_widget = *main_widget.find_descendant_of_type_named("palette_widget"); - palette_widget.set_image_editor(image_editor); + + auto current_image_editor = [&]() -> PixelPaint::ImageEditor* { + if (!tab_widget.active_widget()) + return nullptr; + return downcast(tab_widget.active_widget()); + }; + + Function)> create_new_editor; auto& layer_list_widget = *main_widget.find_descendant_of_type_named("layer_list_widget"); layer_list_widget.on_layer_select = [&](auto* layer) { - image_editor.set_active_layer(layer); + if (auto* editor = current_image_editor()) + editor->set_active_layer(layer); }; auto& layer_properties_widget = *main_widget.find_descendant_of_type_named("layer_properties_widget"); @@ -74,7 +83,8 @@ int main(int argc, char** argv) auto& tool_properties_widget = *main_widget.find_descendant_of_type_named("tool_properties_widget"); toolbox.on_tool_selection = [&](auto* tool) { - image_editor.set_active_tool(tool); + if (auto* editor = current_image_editor()) + editor->set_active_tool(tool); tool_properties_widget.set_active_tool(tool); }; @@ -88,7 +98,7 @@ int main(int argc, char** argv) image->add_layer(*bg_layer); bg_layer->bitmap().fill(Color::White); - image_editor.set_image(image); + auto& image_editor = create_new_editor(*image); layer_list_widget.set_image(image); image_editor.set_active_layer(bg_layer); } @@ -102,7 +112,7 @@ int main(int argc, char** argv) return; } auto& image = *image_or_error.value(); - image_editor.set_image(image); + create_new_editor(image); layer_list_widget.set_image(&image); }; @@ -114,12 +124,15 @@ int main(int argc, char** argv) }); auto save_image_as_action = GUI::CommonActions::make_save_as_action([&](auto&) { - if (!image_editor.image()) + auto* editor = current_image_editor(); + if (!editor) + return; + if (!editor->image()) return; auto save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "pp"); if (!save_path.has_value()) return; - auto result = image_editor.image()->write_to_file(save_path.value()); + auto result = editor->image()->write_to_file(save_path.value()); if (result.is_error()) { GUI::MessageBox::show_error(window, String::formatted("Could not save {}: {}", save_path.value(), result.error())); return; @@ -136,12 +149,15 @@ int main(int argc, char** argv) export_submenu.add_action( GUI::Action::create( "As &BMP", [&](auto&) { - if (!image_editor.image()) + auto* editor = current_image_editor(); + if (!editor) + return; + if (!editor->image()) return; auto save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "bmp"); if (!save_path.has_value()) return; - auto result = image_editor.image()->export_bmp_to_file(save_path.value()); + auto result = editor->image()->export_bmp_to_file(save_path.value()); if (result.is_error()) GUI::MessageBox::show_error(window, String::formatted("Export to BMP failed: {}", result.error())); }, @@ -149,12 +165,13 @@ int main(int argc, char** argv) export_submenu.add_action( GUI::Action::create( "As &PNG", [&](auto&) { - if (!image_editor.image()) + auto* editor = current_image_editor(); + if (!editor->image()) return; auto save_path = GUI::FilePicker::get_save_filepath(window, "untitled", "png"); if (!save_path.has_value()) return; - auto result = image_editor.image()->export_bmp_to_file(save_path.value()); + auto result = editor->image()->export_bmp_to_file(save_path.value()); if (result.is_error()) GUI::MessageBox::show_error(window, String::formatted("Export to PNG failed: {}", result.error())); }, @@ -168,12 +185,15 @@ int main(int argc, char** argv) auto& edit_menu = menubar->add_menu("&Edit"); auto copy_action = GUI::CommonActions::make_copy_action([&](auto&) { - VERIFY(image_editor.image()); - if (!image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + VERIFY(editor->image()); + if (!editor->active_layer()) { dbgln("Cannot copy with no active layer selected"); return; } - auto bitmap = image_editor.active_layer()->try_copy_bitmap(image_editor.selection()); + auto bitmap = editor->active_layer()->try_copy_bitmap(editor->selection()); if (!bitmap) { dbgln("try_copy() from Layer failed"); return; @@ -182,16 +202,19 @@ int main(int argc, char** argv) }); auto paste_action = GUI::CommonActions::make_paste_action([&](auto&) { - VERIFY(image_editor.image()); + auto* editor = current_image_editor(); + if (!editor) + return; + VERIFY(editor->image()); auto bitmap = GUI::Clipboard::the().bitmap(); if (!bitmap) return; - auto layer = PixelPaint::Layer::try_create_with_bitmap(*image_editor.image(), *bitmap, "Pasted layer"); + auto layer = PixelPaint::Layer::try_create_with_bitmap(*editor->image(), *bitmap, "Pasted layer"); VERIFY(layer); - image_editor.image()->add_layer(*layer); - image_editor.set_active_layer(layer); - image_editor.selection().clear(); + editor->image()->add_layer(*layer); + editor->set_active_layer(layer); + editor->selection().clear(); }); GUI::Clipboard::the().on_change = [&](auto& mime_type) { paste_action->set_enabled(mime_type == "image/x-serenityos"); @@ -202,42 +225,48 @@ int main(int argc, char** argv) edit_menu.add_action(paste_action); auto undo_action = GUI::CommonActions::make_undo_action([&](auto&) { - VERIFY(image_editor.image()); - image_editor.undo(); + if (auto* editor = current_image_editor()) + editor->undo(); }); edit_menu.add_action(undo_action); auto redo_action = GUI::CommonActions::make_redo_action([&](auto&) { - VERIFY(image_editor.image()); - image_editor.redo(); + if (auto* editor = current_image_editor()) + editor->redo(); }); edit_menu.add_action(redo_action); edit_menu.add_separator(); edit_menu.add_action(GUI::CommonActions::make_select_all_action([&](auto&) { - VERIFY(image_editor.image()); - if (!image_editor.active_layer()) + auto* editor = current_image_editor(); + if (!editor->active_layer()) return; - image_editor.selection().set(image_editor.active_layer()->relative_rect()); + editor->selection().set(editor->active_layer()->relative_rect()); })); edit_menu.add_action(GUI::Action::create( "Clear &Selection", { Mod_Ctrl | Mod_Shift, Key_A }, [&](auto&) { - image_editor.selection().clear(); + if (auto* editor = current_image_editor()) + editor->selection().clear(); }, window)); edit_menu.add_separator(); edit_menu.add_action(GUI::Action::create( "&Swap Colors", { Mod_None, Key_X }, [&](auto&) { - auto old_primary_color = image_editor.primary_color(); - image_editor.set_primary_color(image_editor.secondary_color()); - image_editor.set_secondary_color(old_primary_color); + auto* editor = current_image_editor(); + if (!editor) + return; + auto old_primary_color = editor->primary_color(); + editor->set_primary_color(editor->secondary_color()); + editor->set_secondary_color(old_primary_color); }, window)); edit_menu.add_action(GUI::Action::create( "&Default Colors", { Mod_None, Key_D }, [&](auto&) { - image_editor.set_primary_color(Color::Black); - image_editor.set_secondary_color(Color::White); + if (auto* editor = current_image_editor()) { + editor->set_primary_color(Color::Black); + editor->set_secondary_color(Color::White); + } }, window)); @@ -245,19 +274,22 @@ int main(int argc, char** argv) auto zoom_in_action = GUI::CommonActions::make_zoom_in_action( [&](auto&) { - image_editor.scale_by(0.1f); + if (auto* editor = current_image_editor()) + editor->scale_by(0.1f); }, window); auto zoom_out_action = GUI::CommonActions::make_zoom_out_action( [&](auto&) { - image_editor.scale_by(-0.1f); + if (auto* editor = current_image_editor()) + editor->scale_by(-0.1f); }, window); auto reset_zoom_action = GUI::CommonActions::make_reset_zoom_action( [&](auto&) { - image_editor.reset_scale_and_position(); + if (auto* editor = current_image_editor()) + editor->reset_scale_and_position(); }, window); @@ -275,15 +307,18 @@ int main(int argc, char** argv) auto& layer_menu = menubar->add_menu("&Layer"); layer_menu.add_action(GUI::Action::create( "New &Layer...", { Mod_Ctrl | Mod_Shift, Key_N }, [&](auto&) { - auto dialog = PixelPaint::CreateNewLayerDialog::construct(image_editor.image()->size(), window); + auto* editor = current_image_editor(); + if (!editor) + return; + auto dialog = PixelPaint::CreateNewLayerDialog::construct(editor->image()->size(), window); if (dialog->exec() == GUI::Dialog::ExecOK) { - auto layer = PixelPaint::Layer::try_create_with_size(*image_editor.image(), dialog->layer_size(), dialog->layer_name()); + auto layer = PixelPaint::Layer::try_create_with_size(*editor->image(), dialog->layer_size(), dialog->layer_name()); if (!layer) { GUI::MessageBox::show_error(window, String::formatted("Unable to create layer with size {}", dialog->size().to_string())); return; } - image_editor.image()->add_layer(layer.release_nonnull()); - image_editor.layers_did_change(); + editor->image()->add_layer(layer.release_nonnull()); + editor->layers_did_change(); } }, window)); @@ -312,28 +347,37 @@ int main(int argc, char** argv) layer_menu.add_separator(); layer_menu.add_action(GUI::Action::create( "Move Active Layer &Up", { Mod_Ctrl, Key_PageUp }, [&](auto&) { - auto active_layer = image_editor.active_layer(); + auto* editor = current_image_editor(); + if (!editor) + return; + auto active_layer = editor->active_layer(); if (!active_layer) return; - image_editor.image()->move_layer_up(*active_layer); + editor->image()->move_layer_up(*active_layer); }, window)); layer_menu.add_action(GUI::Action::create( "Move Active Layer &Down", { Mod_Ctrl, Key_PageDown }, [&](auto&) { - auto active_layer = image_editor.active_layer(); + auto* editor = current_image_editor(); + if (!editor) + return; + auto active_layer = editor->active_layer(); if (!active_layer) return; - image_editor.image()->move_layer_down(*active_layer); + editor->image()->move_layer_down(*active_layer); }, window)); layer_menu.add_separator(); layer_menu.add_action(GUI::Action::create( "&Remove Active Layer", { Mod_Ctrl, Key_D }, [&](auto&) { - auto active_layer = image_editor.active_layer(); + auto* editor = current_image_editor(); + if (!editor) + return; + auto active_layer = editor->active_layer(); if (!active_layer) return; - image_editor.image()->remove_layer(*active_layer); - image_editor.set_active_layer(nullptr); + editor->image()->remove_layer(*active_layer); + editor->set_active_layer(nullptr); }, window)); @@ -342,77 +386,101 @@ int main(int argc, char** argv) auto& edge_detect_submenu = spatial_filters_menu.add_submenu("&Edge Detect"); edge_detect_submenu.add_action(GUI::Action::create("Laplacian (&Cardinal)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::LaplacianFilter filter; if (auto parameters = PixelPaint::FilterParameters::get(false)) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); edge_detect_submenu.add_action(GUI::Action::create("Laplacian (&Diagonal)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::LaplacianFilter filter; if (auto parameters = PixelPaint::FilterParameters::get(true)) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); auto& blur_submenu = spatial_filters_menu.add_submenu("&Blur and Sharpen"); blur_submenu.add_action(GUI::Action::create("&Gaussian Blur (3x3)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::SpatialGaussianBlurFilter<3> filter; if (auto parameters = PixelPaint::FilterParameters>::get()) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); blur_submenu.add_action(GUI::Action::create("G&aussian Blur (5x5)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::SpatialGaussianBlurFilter<5> filter; if (auto parameters = PixelPaint::FilterParameters>::get()) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); blur_submenu.add_action(GUI::Action::create("&Box Blur (3x3)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::BoxBlurFilter<3> filter; if (auto parameters = PixelPaint::FilterParameters>::get()) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); blur_submenu.add_action(GUI::Action::create("B&ox Blur (5x5)", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::BoxBlurFilter<5> filter; if (auto parameters = PixelPaint::FilterParameters>::get()) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); blur_submenu.add_action(GUI::Action::create("&Sharpen", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::SharpenFilter filter; if (auto parameters = PixelPaint::FilterParameters::get()) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); spatial_filters_menu.add_separator(); spatial_filters_menu.add_action(GUI::Action::create("Generic 5x5 &Convolution", [&](auto&) { - if (auto* layer = image_editor.active_layer()) { + auto* editor = current_image_editor(); + if (!editor) + return; + if (auto* layer = editor->active_layer()) { Gfx::GenericConvolutionFilter<5> filter; if (auto parameters = PixelPaint::FilterParameters>::get(window)) { filter.apply(layer->bitmap(), layer->rect(), layer->bitmap(), layer->rect(), *parameters); - image_editor.did_complete_action(); + editor->did_complete_action(); } } })); @@ -436,9 +504,35 @@ int main(int argc, char** argv) toolbar.add_action(zoom_out_action); toolbar.add_action(reset_zoom_action); - image_editor.on_active_layer_change = [&](auto* layer) { - layer_list_widget.set_selected_layer(layer); - layer_properties_widget.set_layer(layer); + create_new_editor = [&](NonnullRefPtr image) -> PixelPaint::ImageEditor& { + auto& image_editor = tab_widget.add_tab("Untitled", image); + + image_editor.on_active_layer_change = [&](auto* layer) { + if (current_image_editor() != &image_editor) + return; + layer_list_widget.set_selected_layer(layer); + layer_properties_widget.set_layer(layer); + }; + + if (image->layer_count()) + image_editor.set_active_layer(&image->layer(0)); + + image_editor.set_focus(true); + return image_editor; + }; + + tab_widget.on_change = [&](auto& widget) { + auto& image_editor = downcast(widget); + palette_widget.set_image_editor(image_editor); + layer_list_widget.set_image(image_editor.image()); + layer_properties_widget.set_layer(nullptr); + // FIXME: This is badly factored. It transfers tools from the previously active editor to the new one. + toolbox.template for_each_tool([&](auto& tool) { + if (tool.editor()) { + tool.editor()->set_active_tool(nullptr); + image_editor.set_active_tool(&tool); + } + }); }; auto image_file_real_path = Core::File::real_path_for(image_file); @@ -454,8 +548,8 @@ int main(int argc, char** argv) layer_list_widget.set_image(image); - image_editor.set_image(image); - image_editor.set_active_layer(bg_layer); + auto& editor = create_new_editor(*image); + editor.set_active_layer(bg_layer); } auto& statusbar = *main_widget.find_descendant_of_type_named("statusbar");