diff --git a/Userland/Games/Minesweeper/CMakeLists.txt b/Userland/Games/Minesweeper/CMakeLists.txt index 8d08e56e17..fc74b7124b 100644 --- a/Userland/Games/Minesweeper/CMakeLists.txt +++ b/Userland/Games/Minesweeper/CMakeLists.txt @@ -4,7 +4,11 @@ serenity_component( TARGETS Minesweeper ) +compile_gml(MinesweeperCustomGameWindow.gml MinesweeperCustomGameWindowGML.h minesweeper_custom_game_window_gml) + set(SOURCES + MinesweeperCustomGameWindowGML.h + CustomGameDialog.cpp Field.cpp main.cpp ) diff --git a/Userland/Games/Minesweeper/CustomGameDialog.cpp b/Userland/Games/Minesweeper/CustomGameDialog.cpp new file mode 100644 index 0000000000..4e69303747 --- /dev/null +++ b/Userland/Games/Minesweeper/CustomGameDialog.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021, Pedro Pereira + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "CustomGameDialog.h" +#include "Field.h" +#include + +int CustomGameDialog::show(GUI::Window* parent_window, Field& field) +{ + auto dialog = CustomGameDialog::construct(parent_window); + + if (parent_window) { + dialog->set_icon(parent_window->icon()); + dialog->center_within(*parent_window); + } + + dialog->m_columns_spinbox->set_value(field.columns()); + dialog->m_rows_spinbox->set_value(field.rows()); + dialog->m_mines_spinbox->set_value(field.mine_count()); + + auto result = dialog->exec(); + + if (result != GUI::Dialog::ExecOK) + return result; + + field.set_field_size(dialog->m_rows_spinbox->value(), dialog->m_columns_spinbox->value(), dialog->m_mines_spinbox->value()); + + return GUI::Dialog::ExecOK; +} + +void CustomGameDialog::set_max_mines() +{ + // Generating a field with > 50% mines takes too long. + // FIXME: Allow higher amount of mines to be placed. + m_mines_spinbox->set_max((m_rows_spinbox->value() * m_columns_spinbox->value()) / 2); +} + +CustomGameDialog::CustomGameDialog(Window* parent_window) + : Dialog(parent_window) +{ + resize(305, 90); + center_on_screen(); + set_resizable(false); + set_title("Custom game"); + + auto& main_widget = set_main_widget(); + if (!main_widget.load_from_gml(minesweeper_custom_game_window_gml)) + VERIFY_NOT_REACHED(); + + m_columns_spinbox = *main_widget.find_descendant_of_type_named("columns_spinbox"); + m_rows_spinbox = *main_widget.find_descendant_of_type_named("rows_spinbox"); + m_mines_spinbox = *main_widget.find_descendant_of_type_named("mines_spinbox"); + m_ok_button = *main_widget.find_descendant_of_type_named("ok_button"); + m_cancel_button = *main_widget.find_descendant_of_type_named("cancel_button"); + + m_columns_spinbox->on_change = [this](auto) { + set_max_mines(); + }; + + m_rows_spinbox->on_change = [this](auto) { + set_max_mines(); + }; + + m_ok_button->on_click = [this](auto) { + done(ExecResult::ExecOK); + }; + + m_cancel_button->on_click = [this](auto) { + done(ExecResult::ExecCancel); + }; + + set_max_mines(); +} + +CustomGameDialog::~CustomGameDialog() +{ +} diff --git a/Userland/Games/Minesweeper/CustomGameDialog.h b/Userland/Games/Minesweeper/CustomGameDialog.h new file mode 100644 index 0000000000..ef0d4f0eea --- /dev/null +++ b/Userland/Games/Minesweeper/CustomGameDialog.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021, Pedro Pereira + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +class Field; + +class CustomGameDialog : public GUI::Dialog { + C_OBJECT(CustomGameDialog); + +public: + static int show(GUI::Window* parent_window, Field& field); + +private: + CustomGameDialog(GUI::Window* parent_window); + virtual ~CustomGameDialog() override; + + void set_max_mines(); + + RefPtr m_ok_button; + RefPtr m_cancel_button; + RefPtr m_columns_spinbox; + RefPtr m_rows_spinbox; + RefPtr m_mines_spinbox; +}; diff --git a/Userland/Games/Minesweeper/MinesweeperCustomGameWindow.gml b/Userland/Games/Minesweeper/MinesweeperCustomGameWindow.gml new file mode 100644 index 0000000000..419c190b9f --- /dev/null +++ b/Userland/Games/Minesweeper/MinesweeperCustomGameWindow.gml @@ -0,0 +1,80 @@ +@GUI::Widget { + fill_with_background_color: true + + layout: @GUI::VerticalBoxLayout { + margins: [4] + } + + @GUI::GroupBox { + title: "Field" + autosize: true + + layout: @GUI::HorizontalBoxLayout { + margins: [16, 6, 6] + } + + @GUI::Label { + text: "Columns: " + autosize: true + } + + @GUI::SpinBox { + name: "columns_spinbox" + min: 10 + max: 50 + fixed_width: 40 + } + + @GUI::VerticalSeparator { + } + + @GUI::Label { + text: "Rows: " + autosize: true + } + + @GUI::SpinBox { + name: "rows_spinbox" + min: 10 + max: 50 + fixed_width: 40 + } + + @GUI::VerticalSeparator { + } + + @GUI::Label { + text: "Mines: " + autosize: true + } + + @GUI::SpinBox { + name: "mines_spinbox" + min: 1 + max: 2500 + fixed_width: 50 + } + } + + @GUI::Widget { + max_height: 24 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Widget { + } + + @GUI::Button { + name: "ok_button" + text: "OK" + max_width: 75 + } + + @GUI::Button { + name: "cancel_button" + text: "Cancel" + max_width: 75 + } + } +} diff --git a/Userland/Games/Minesweeper/main.cpp b/Userland/Games/Minesweeper/main.cpp index d12e31f99f..47bc685235 100644 --- a/Userland/Games/Minesweeper/main.cpp +++ b/Userland/Games/Minesweeper/main.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include "CustomGameDialog.h" #include "Field.h" #include #include @@ -131,6 +132,10 @@ int main(int argc, char** argv) difficulty_menu.add_action(GUI::Action::create("&Madwoman", { Mod_Ctrl, Key_M }, [&](auto&) { field.set_field_size(32, 60, 350); })); + difficulty_menu.add_separator(); + difficulty_menu.add_action(GUI::Action::create("&Custom game...", { Mod_Ctrl, Key_C }, [&](auto&) { + CustomGameDialog::show(window, field); + })); auto& help_menu = window->add_menu("&Help"); help_menu.add_action(GUI::CommonActions::make_about_action("Minesweeper", app_icon, window));