2020-01-18 09:38:21 +01:00
/*
* Copyright ( c ) 2018 - 2020 , Andreas Kling < kling @ serenityos . org >
* 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 .
*/
2020-05-13 21:14:39 +02:00
# include "CreateNewLayerDialog.h"
2020-05-12 22:22:45 +02:00
# include "Image.h"
# include "ImageEditor.h"
# include "Layer.h"
2020-05-25 22:38:27 +02:00
# include "LayerListWidget.h"
2019-06-10 19:54:09 +02:00
# include "PaletteWidget.h"
2020-05-13 19:30:07 +02:00
# include "Tool.h"
2019-06-12 05:58:31 +02:00
# include "ToolboxWidget.h"
2020-02-06 20:33:02 +01:00
# include <LibGUI/AboutDialog.h>
# include <LibGUI/Action.h>
# include <LibGUI/Application.h>
# include <LibGUI/BoxLayout.h>
# include <LibGUI/FilePicker.h>
2020-06-04 13:30:05 +03:00
# include <LibGUI/Icon.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Menu.h>
# include <LibGUI/MenuBar.h>
# include <LibGUI/MessageBox.h>
2020-05-12 23:00:23 +02:00
# include <LibGUI/TableView.h>
2020-02-06 20:33:02 +01:00
# include <LibGUI/Window.h>
2020-02-16 09:17:49 +01:00
# include <LibGfx/Bitmap.h>
2020-01-13 12:22:49 +01:00
# include <stdio.h>
2019-06-10 19:29:33 +02:00
int main ( int argc , char * * argv )
{
2020-02-28 19:48:29 +01:00
if ( pledge ( " stdio thread shared_buffer accept rpath unix wpath cpath fattr " , nullptr ) < 0 ) {
2020-01-13 12:22:49 +01:00
perror ( " pledge " ) ;
return 1 ;
}
2020-07-04 14:05:19 +02:00
auto app = GUI : : Application : : construct ( argc , argv ) ;
2019-06-10 19:29:33 +02:00
2020-02-28 19:48:29 +01:00
if ( pledge ( " stdio thread shared_buffer accept rpath wpath cpath " , nullptr ) < 0 ) {
2020-01-13 12:22:49 +01:00
perror ( " pledge " ) ;
return 1 ;
}
2020-06-04 13:30:05 +03:00
auto app_icon = GUI : : Icon : : default_icon ( " app-pixel-paint " ) ;
2020-02-02 15:07:41 +01:00
auto window = GUI : : Window : : construct ( ) ;
2020-05-20 20:35:35 +02:00
window - > set_title ( " PixelPaint " ) ;
2020-05-13 22:12:03 +02:00
window - > set_rect ( 40 , 100 , 950 , 570 ) ;
2020-06-04 13:30:05 +03:00
window - > set_icon ( app_icon . bitmap_for_size ( 16 ) ) ;
2019-06-10 19:29:33 +02:00
2020-03-04 09:46:23 +01:00
auto & horizontal_container = window - > set_main_widget < GUI : : Widget > ( ) ;
horizontal_container . set_layout < GUI : : HorizontalBoxLayout > ( ) ;
horizontal_container . layout ( ) - > set_spacing ( 0 ) ;
2019-06-10 19:54:09 +02:00
2020-05-20 20:35:35 +02:00
auto & toolbox = horizontal_container . add < PixelPaint : : ToolboxWidget > ( ) ;
2019-06-12 05:58:31 +02:00
2020-03-04 19:07:55 +01:00
auto & vertical_container = horizontal_container . add < GUI : : Widget > ( ) ;
vertical_container . set_layout < GUI : : VerticalBoxLayout > ( ) ;
vertical_container . layout ( ) - > set_spacing ( 0 ) ;
2019-06-12 05:58:31 +02:00
2020-05-20 20:35:35 +02:00
auto & image_editor = vertical_container . add < PixelPaint : : ImageEditor > ( ) ;
2020-05-12 22:22:45 +02:00
image_editor . set_focus ( true ) ;
2020-05-12 23:44:46 +02:00
toolbox . on_tool_selection = [ & ] ( auto * tool ) {
image_editor . set_active_tool ( tool ) ;
} ;
2020-05-20 20:35:35 +02:00
vertical_container . add < PixelPaint : : PaletteWidget > ( image_editor ) ;
2019-06-10 19:29:33 +02:00
2020-05-12 23:00:23 +02:00
auto & right_panel = horizontal_container . add < GUI : : Widget > ( ) ;
2020-05-25 22:38:27 +02:00
right_panel . set_fill_with_background_color ( true ) ;
2020-05-12 23:00:23 +02:00
right_panel . set_size_policy ( GUI : : SizePolicy : : Fixed , GUI : : SizePolicy : : Fill ) ;
right_panel . set_preferred_size ( 230 , 0 ) ;
right_panel . set_layout < GUI : : VerticalBoxLayout > ( ) ;
2020-05-25 22:38:27 +02:00
auto & layer_list_widget = right_panel . add < PixelPaint : : LayerListWidget > ( ) ;
2019-06-10 19:29:33 +02:00
window - > show ( ) ;
2019-06-10 21:05:20 +02:00
2020-04-21 16:01:00 +02:00
auto menubar = GUI : : MenuBar : : construct ( ) ;
2020-05-20 20:35:35 +02:00
auto & app_menu = menubar - > add_menu ( " PixelPaint " ) ;
2019-10-01 00:18:20 -05:00
2020-04-04 12:18:40 +02:00
app_menu . add_action ( GUI : : CommonActions : : make_open_action ( [ & ] ( auto & ) {
2020-02-02 15:07:41 +01:00
Optional < String > open_path = GUI : : FilePicker : : get_open_filepath ( ) ;
2019-10-01 00:18:20 -05:00
if ( ! open_path . has_value ( ) )
return ;
2020-02-06 13:39:17 +01:00
auto bitmap = Gfx : : Bitmap : : load_from_file ( open_path . value ( ) ) ;
2019-10-01 00:18:20 -05:00
if ( ! bitmap ) {
2020-02-02 15:07:41 +01:00
GUI : : MessageBox : : show ( String : : format ( " Failed to load '%s' " , open_path . value ( ) . characters ( ) ) , " Open failed " , GUI : : MessageBox : : Type : : Error , GUI : : MessageBox : : InputType : : OK , window ) ;
2019-10-01 00:18:20 -05:00
return ;
}
} ) ) ;
2020-04-04 12:18:40 +02:00
app_menu . add_separator ( ) ;
app_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( auto & ) {
2020-07-04 16:52:01 +02:00
GUI : : Application : : the ( ) - > quit ( ) ;
2019-06-10 21:05:20 +02:00
return ;
} ) ) ;
2020-04-04 12:18:40 +02:00
menubar - > add_menu ( " Edit " ) ;
2019-06-10 21:05:20 +02:00
2020-05-13 19:30:07 +02:00
auto & tool_menu = menubar - > add_menu ( " Tool " ) ;
toolbox . for_each_tool ( [ & ] ( auto & tool ) {
if ( tool . action ( ) )
tool_menu . add_action ( * tool . action ( ) ) ;
return IterationDecision : : Continue ;
} ) ;
2020-05-13 21:14:39 +02:00
auto & layer_menu = menubar - > add_menu ( " Layer " ) ;
2020-05-25 22:38:27 +02:00
layer_menu . add_action ( GUI : : Action : : create (
" Create new layer... " , { Mod_Ctrl | Mod_Shift , Key_N } , [ & ] ( auto & ) {
auto dialog = PixelPaint : : CreateNewLayerDialog : : construct ( image_editor . image ( ) - > size ( ) , window ) ;
if ( dialog - > exec ( ) = = GUI : : Dialog : : ExecOK ) {
auto layer = PixelPaint : : Layer : : create_with_size ( dialog - > layer_size ( ) , dialog - > layer_name ( ) ) ;
if ( ! layer ) {
GUI : : MessageBox : : show_error ( String : : format ( " Unable to create layer with size %s " , dialog - > size ( ) . to_string ( ) . characters ( ) ) ) ;
return ;
}
image_editor . image ( ) - > add_layer ( layer . release_nonnull ( ) ) ;
image_editor . layers_did_change ( ) ;
2020-05-13 21:14:39 +02:00
}
2020-05-25 22:38:27 +02:00
} ,
window ) ) ;
2020-05-13 21:14:39 +02:00
2020-05-13 21:25:13 +02:00
layer_menu . add_separator ( ) ;
2020-05-25 22:38:27 +02:00
layer_menu . add_action ( GUI : : Action : : create (
" Select previous layer " , { 0 , Key_PageUp } , [ & ] ( auto & ) {
2020-05-26 09:51:28 +02:00
layer_list_widget . move_selection ( 1 ) ;
2020-05-25 22:38:27 +02:00
} ,
window ) ) ;
layer_menu . add_action ( GUI : : Action : : create (
" Select next layer " , { 0 , Key_PageDown } , [ & ] ( auto & ) {
2020-05-26 09:51:28 +02:00
layer_list_widget . move_selection ( - 1 ) ;
2020-05-25 22:38:27 +02:00
} ,
window ) ) ;
layer_menu . add_action ( GUI : : Action : : create (
" Select top layer " , { 0 , Key_Home } , [ & ] ( auto & ) {
2020-05-26 09:51:28 +02:00
layer_list_widget . select_top_layer ( ) ;
2020-05-25 22:38:27 +02:00
} ,
window ) ) ;
layer_menu . add_action ( GUI : : Action : : create (
" Select bottom layer " , { 0 , Key_End } , [ & ] ( auto & ) {
2020-05-26 09:51:28 +02:00
layer_list_widget . select_bottom_layer ( ) ;
2020-05-25 22:38:27 +02:00
} ,
window ) ) ;
2020-05-14 14:18:47 +02:00
layer_menu . add_separator ( ) ;
2020-05-25 22:38:27 +02:00
layer_menu . add_action ( GUI : : Action : : create (
" Move active layer up " , { Mod_Ctrl , Key_PageUp } , [ & ] ( auto & ) {
auto active_layer = image_editor . active_layer ( ) ;
if ( ! active_layer )
return ;
image_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 ( ) ;
if ( ! active_layer )
return ;
image_editor . image ( ) - > move_layer_down ( * active_layer ) ;
} ,
window ) ) ;
2020-05-14 14:22:30 +02:00
layer_menu . add_separator ( ) ;
2020-05-25 22:38:27 +02:00
layer_menu . add_action ( GUI : : Action : : create (
" Remove active layer " , { Mod_Ctrl , Key_D } , [ & ] ( auto & ) {
auto active_layer = image_editor . active_layer ( ) ;
if ( ! active_layer )
return ;
image_editor . image ( ) - > remove_layer ( * active_layer ) ;
2020-05-29 01:58:22 -04:00
image_editor . set_active_layer ( nullptr ) ;
2020-05-25 22:38:27 +02:00
} ,
window ) ) ;
2020-05-13 21:25:13 +02:00
2020-04-04 12:18:40 +02:00
auto & help_menu = menubar - > add_menu ( " Help " ) ;
help_menu . add_action ( GUI : : Action : : create ( " About " , [ & ] ( auto & ) {
2020-06-04 13:30:05 +03:00
GUI : : AboutDialog : : show ( " PixelPaint " , app_icon . bitmap_for_size ( 32 ) , window ) ;
2019-06-10 21:05:20 +02:00
} ) ) ;
2020-07-04 14:05:19 +02:00
app - > set_menubar ( move ( menubar ) ) ;
2019-06-10 21:05:20 +02:00
2020-05-26 09:51:28 +02:00
image_editor . on_active_layer_change = [ & ] ( auto * layer ) {
layer_list_widget . set_selected_layer ( layer ) ;
2020-05-13 21:46:19 +02:00
} ;
2020-05-20 20:35:35 +02:00
auto image = PixelPaint : : Image : : create_with_size ( { 640 , 480 } ) ;
2020-05-12 22:22:45 +02:00
2020-05-20 20:35:35 +02:00
auto bg_layer = PixelPaint : : Layer : : create_with_size ( { 640 , 480 } , " Background " ) ;
2020-05-12 22:22:45 +02:00
image - > add_layer ( * bg_layer ) ;
2020-05-13 22:28:33 +02:00
bg_layer - > bitmap ( ) . fill ( Color : : White ) ;
2020-05-12 22:22:45 +02:00
2020-05-25 22:38:27 +02:00
auto fg_layer1 = PixelPaint : : Layer : : create_with_size ( { 200 , 200 } , " FG Layer 1 " ) ;
fg_layer1 - > set_location ( { 50 , 50 } ) ;
image - > add_layer ( * fg_layer1 ) ;
fg_layer1 - > bitmap ( ) . fill ( Color : : Yellow ) ;
auto fg_layer2 = PixelPaint : : Layer : : create_with_size ( { 100 , 100 } , " FG Layer 2 " ) ;
fg_layer2 - > set_location ( { 300 , 300 } ) ;
image - > add_layer ( * fg_layer2 ) ;
fg_layer2 - > bitmap ( ) . fill ( Color : : Blue ) ;
2020-05-26 09:51:28 +02:00
layer_list_widget . on_layer_select = [ & ] ( auto * layer ) {
image_editor . set_active_layer ( layer ) ;
2020-05-12 23:00:23 +02:00
} ;
2020-05-25 22:38:27 +02:00
layer_list_widget . set_image ( image ) ;
2020-05-12 22:22:45 +02:00
image_editor . set_image ( image ) ;
2020-05-13 22:28:33 +02:00
image_editor . set_active_layer ( bg_layer ) ;
2020-05-12 22:22:45 +02:00
2020-07-04 14:05:19 +02:00
return app - > exec ( ) ;
2019-06-10 19:29:33 +02:00
}