mirror of
https://github.com/RGBCube/serenity
synced 2025-07-28 17:37:47 +00:00
Games: Add GameOfLife
This patch introduces a new game based on Conway's Game of Life.
This commit is contained in:
parent
e4f61c6f28
commit
d99991e39c
18 changed files with 667 additions and 288 deletions
132
Userland/Games/GameOfLife/Board.cpp
Normal file
132
Userland/Games/GameOfLife/Board.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andres Crucitti <dasc495@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Board.h"
|
||||
#include <AK/Random.h>
|
||||
#include <time.h>
|
||||
|
||||
Board::Board(size_t rows, size_t columns)
|
||||
: m_columns(columns)
|
||||
, m_rows(rows)
|
||||
{
|
||||
m_cells.resize(total_size());
|
||||
for (size_t i = 0; i < total_size(); ++i) {
|
||||
m_cells[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
Board::~Board()
|
||||
{
|
||||
}
|
||||
|
||||
void Board::run_generation()
|
||||
{
|
||||
m_stalled = true;
|
||||
Vector<bool> new_cells;
|
||||
new_cells.resize(total_size());
|
||||
|
||||
for (size_t i = 0; i < total_size(); ++i) {
|
||||
bool old_val = m_cells[i];
|
||||
new_cells[i] = calculate_next_value(i);
|
||||
if (old_val != new_cells[i]) {
|
||||
m_stalled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_stalled)
|
||||
return;
|
||||
|
||||
m_cells = new_cells;
|
||||
}
|
||||
|
||||
bool Board::calculate_next_value(size_t index) const
|
||||
{
|
||||
size_t row = index / columns();
|
||||
size_t column = index % columns();
|
||||
|
||||
int top_left = cell(row - 1, column - 1);
|
||||
int top_mid = cell(row - 1, column);
|
||||
int top_right = cell(row - 1, column + 1);
|
||||
int left = cell(row, column - 1);
|
||||
int right = cell(row, column + 1);
|
||||
int bottom_left = cell(row + 1, column - 1);
|
||||
int bottom_mid = cell(row + 1, column);
|
||||
int bottom_right = cell(row + 1, column + 1);
|
||||
|
||||
int sum = top_left + top_mid + top_right + left + right + bottom_left + bottom_mid + bottom_right;
|
||||
|
||||
bool current = m_cells[index];
|
||||
bool new_value = current;
|
||||
|
||||
if (current) {
|
||||
if (sum < 2 || sum > 3)
|
||||
new_value = false;
|
||||
} else {
|
||||
if (sum == 3)
|
||||
new_value = true;
|
||||
}
|
||||
|
||||
return new_value;
|
||||
}
|
||||
|
||||
void Board::clear()
|
||||
{
|
||||
for (size_t i = 0; i < total_size(); ++i)
|
||||
set_cell(i, false);
|
||||
}
|
||||
|
||||
void Board::randomize()
|
||||
{
|
||||
for (size_t i = 0; i < total_size(); ++i)
|
||||
set_cell(i, get_random<u32>() % 2);
|
||||
}
|
||||
|
||||
void Board::toggle_cell(size_t index)
|
||||
{
|
||||
VERIFY(index < total_size());
|
||||
|
||||
m_cells[index] = !m_cells[index];
|
||||
}
|
||||
|
||||
void Board::toggle_cell(size_t row, size_t column)
|
||||
{
|
||||
VERIFY(column < total_size() && row < total_size());
|
||||
|
||||
size_t index = calculate_index(row, column);
|
||||
set_cell(index, !m_cells[index]);
|
||||
}
|
||||
|
||||
void Board::set_cell(size_t index, bool on)
|
||||
{
|
||||
VERIFY(index < total_size());
|
||||
|
||||
m_cells[index] = on;
|
||||
}
|
||||
|
||||
void Board::set_cell(size_t row, size_t column, bool on)
|
||||
{
|
||||
VERIFY(column < total_size() && row < total_size());
|
||||
|
||||
size_t index = calculate_index(row, column);
|
||||
set_cell(index, on);
|
||||
}
|
||||
|
||||
bool Board::cell(size_t index) const
|
||||
{
|
||||
if (index > total_size() - 1)
|
||||
return false;
|
||||
|
||||
return m_cells[index];
|
||||
}
|
||||
|
||||
bool Board::cell(size_t row, size_t column) const
|
||||
{
|
||||
if (column > total_size() - 1 || row > total_size() - 1)
|
||||
return false;
|
||||
|
||||
size_t index = calculate_index(row, column);
|
||||
return cell(index);
|
||||
}
|
50
Userland/Games/GameOfLife/Board.h
Normal file
50
Userland/Games/GameOfLife/Board.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andres Crucitti <dasc495@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <stdio.h>
|
||||
|
||||
class Board {
|
||||
public:
|
||||
Board(size_t rows, size_t column);
|
||||
~Board();
|
||||
|
||||
size_t total_size() const { return m_columns * m_rows; }
|
||||
size_t columns() const { return m_columns; }
|
||||
size_t rows() const { return m_rows; }
|
||||
|
||||
size_t calculate_index(size_t row, size_t column) const { return row * m_columns + column; };
|
||||
|
||||
void toggle_cell(size_t index);
|
||||
void toggle_cell(size_t row, size_t column);
|
||||
|
||||
void set_cell(size_t row, size_t column, bool on);
|
||||
void set_cell(size_t index, bool on);
|
||||
|
||||
bool cell(size_t row, size_t column) const;
|
||||
bool cell(size_t index) const;
|
||||
|
||||
const Vector<bool>& cells() const { return m_cells; }
|
||||
|
||||
void run_generation();
|
||||
bool is_stalled() const { return m_stalled; }
|
||||
|
||||
void clear();
|
||||
void randomize();
|
||||
|
||||
private:
|
||||
bool calculate_next_value(size_t index) const;
|
||||
|
||||
size_t m_columns { 1 };
|
||||
size_t m_rows { 1 };
|
||||
|
||||
bool m_stalled { false };
|
||||
|
||||
Vector<bool> m_cells;
|
||||
};
|
172
Userland/Games/GameOfLife/BoardWidget.cpp
Normal file
172
Userland/Games/GameOfLife/BoardWidget.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andres Crucitti <dasc495@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "BoardWidget.h"
|
||||
#include <LibGUI/Painter.h>
|
||||
|
||||
BoardWidget::BoardWidget(size_t rows, size_t columns)
|
||||
{
|
||||
m_timer = add<Core::Timer>();
|
||||
m_timer->stop();
|
||||
m_timer->on_timeout = [this] {
|
||||
run_generation();
|
||||
};
|
||||
m_timer->set_interval(m_running_timer_interval);
|
||||
|
||||
update_board(rows, columns);
|
||||
}
|
||||
|
||||
void BoardWidget::run_generation()
|
||||
{
|
||||
m_board->run_generation();
|
||||
update();
|
||||
if (m_board->is_stalled()) {
|
||||
if (on_stall)
|
||||
on_stall();
|
||||
update();
|
||||
};
|
||||
}
|
||||
|
||||
void BoardWidget::update_board(size_t rows, size_t columns)
|
||||
{
|
||||
set_running(false);
|
||||
|
||||
m_last_cell_toggled = columns * rows;
|
||||
|
||||
if (m_board) {
|
||||
if (columns == m_board->columns() && rows == m_board->rows()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_board = make<Board>(rows, columns);
|
||||
}
|
||||
|
||||
void BoardWidget::set_running_timer_interval(int interval)
|
||||
{
|
||||
if (is_running())
|
||||
return;
|
||||
|
||||
m_running_timer_interval = interval;
|
||||
m_timer->set_interval(m_running_timer_interval);
|
||||
|
||||
if (on_running_state_change)
|
||||
on_running_state_change();
|
||||
}
|
||||
|
||||
void BoardWidget::set_running(bool running)
|
||||
{
|
||||
if (running == m_running)
|
||||
return;
|
||||
|
||||
m_running = running;
|
||||
|
||||
if (m_running) {
|
||||
m_timer->start();
|
||||
} else {
|
||||
m_timer->stop();
|
||||
}
|
||||
|
||||
if (on_running_state_change)
|
||||
on_running_state_change();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void BoardWidget::toggle_cell(size_t index)
|
||||
{
|
||||
if (m_running || !m_toggling_cells || m_last_cell_toggled == index)
|
||||
return;
|
||||
|
||||
m_last_cell_toggled = index;
|
||||
m_board->toggle_cell(index);
|
||||
|
||||
if (on_cell_toggled)
|
||||
on_cell_toggled(m_board, index);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
int BoardWidget::get_cell_size() const
|
||||
{
|
||||
int width = rect().width() / m_board->columns();
|
||||
int height = rect().height() / m_board->rows();
|
||||
|
||||
return min(width, height);
|
||||
}
|
||||
|
||||
Gfx::IntSize BoardWidget::get_board_offset() const
|
||||
{
|
||||
int cell_size = get_cell_size();
|
||||
return {
|
||||
(width() - cell_size * m_board->columns()) / 2,
|
||||
(height() - cell_size * m_board->rows()) / 2,
|
||||
};
|
||||
}
|
||||
|
||||
void BoardWidget::paint_event(GUI::PaintEvent& event)
|
||||
{
|
||||
GUI::Widget::paint_event(event);
|
||||
|
||||
GUI::Painter painter(*this);
|
||||
painter.add_clip_rect(event.rect());
|
||||
painter.fill_rect(event.rect(), Color::Black);
|
||||
|
||||
int cell_size = get_cell_size();
|
||||
Gfx::IntSize board_offset = get_board_offset();
|
||||
|
||||
for (size_t row = 0; row < m_board->rows(); ++row) {
|
||||
for (size_t column = 0; column < m_board->columns(); ++column) {
|
||||
int cell_x = column * cell_size + board_offset.width();
|
||||
int cell_y = row * cell_size + board_offset.height();
|
||||
|
||||
Gfx::Rect cell_rect(cell_x, cell_y, cell_size, cell_size);
|
||||
|
||||
Color border_color = Color::DarkGray;
|
||||
Color fill_color;
|
||||
|
||||
bool on = m_board->cell(row, column);
|
||||
if (on) {
|
||||
fill_color = Color::from_rgb(Gfx::make_rgb(220, 220, 80));
|
||||
} else {
|
||||
fill_color = Color::MidGray;
|
||||
}
|
||||
|
||||
painter.fill_rect(cell_rect, fill_color);
|
||||
if (cell_size > 4) {
|
||||
painter.draw_rect(cell_rect, border_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoardWidget::mousedown_event(GUI::MouseEvent& event)
|
||||
{
|
||||
size_t index = get_index_for_point(event.x(), event.y());
|
||||
set_toggling_cells(true);
|
||||
toggle_cell(index);
|
||||
}
|
||||
|
||||
void BoardWidget::mousemove_event(GUI::MouseEvent& event)
|
||||
{
|
||||
size_t index = get_index_for_point(event.x(), event.y());
|
||||
if (is_toggling()) {
|
||||
if (last_toggled() != index)
|
||||
toggle_cell(index);
|
||||
}
|
||||
}
|
||||
|
||||
void BoardWidget::mouseup_event(GUI::MouseEvent&)
|
||||
{
|
||||
set_toggling_cells(false);
|
||||
}
|
||||
|
||||
size_t BoardWidget::get_index_for_point(int x, int y) const
|
||||
{
|
||||
int cell_size = get_cell_size();
|
||||
Gfx::IntSize board_offset = get_board_offset();
|
||||
return m_board->columns() * ((y - board_offset.height()) / cell_size) + (x - board_offset.width()) / cell_size;
|
||||
}
|
70
Userland/Games/GameOfLife/BoardWidget.h
Normal file
70
Userland/Games/GameOfLife/BoardWidget.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andres Crucitti <dasc495@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Board.h"
|
||||
#include <LibCore/Timer.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
|
||||
class BoardWidget final : public GUI::Widget {
|
||||
C_OBJECT(BoardWidget);
|
||||
|
||||
public:
|
||||
virtual void paint_event(GUI::PaintEvent&) override;
|
||||
virtual void mousemove_event(GUI::MouseEvent&) override;
|
||||
virtual void mouseup_event(GUI::MouseEvent&) override;
|
||||
virtual void mousedown_event(GUI::MouseEvent&) override;
|
||||
|
||||
void set_toggling_cells(bool toggling)
|
||||
{
|
||||
m_toggling_cells = toggling;
|
||||
if (!toggling)
|
||||
m_last_cell_toggled = m_board->total_size();
|
||||
}
|
||||
|
||||
size_t last_toggled() const { return m_last_cell_toggled; }
|
||||
bool is_toggling() const { return m_toggling_cells; }
|
||||
|
||||
void toggle_cell(size_t index);
|
||||
void clear_cells() { m_board->clear(); }
|
||||
void randomize_cells() { m_board->randomize(); }
|
||||
|
||||
int get_cell_size() const;
|
||||
Gfx::IntSize get_board_offset() const;
|
||||
|
||||
size_t get_index_for_point(int x, int y) const;
|
||||
|
||||
void update_board(size_t rows, size_t columns);
|
||||
const Board* board() const { return m_board.ptr(); }
|
||||
|
||||
bool is_running() const { return m_running; }
|
||||
void set_running(bool r);
|
||||
|
||||
void set_toolbar_enabled(bool);
|
||||
|
||||
void run_generation();
|
||||
|
||||
int running_timer_interval() const { return m_running_timer_interval; }
|
||||
void set_running_timer_interval(int interval);
|
||||
|
||||
Function<void()> on_running_state_change;
|
||||
Function<void()> on_stall;
|
||||
Function<void(Board*, size_t)> on_cell_toggled;
|
||||
|
||||
private:
|
||||
BoardWidget(size_t rows, size_t columns);
|
||||
|
||||
bool m_toggling_cells { false };
|
||||
size_t m_last_cell_toggled { 0 };
|
||||
|
||||
OwnPtr<Board> m_board { nullptr };
|
||||
|
||||
bool m_running { false };
|
||||
|
||||
int m_running_timer_interval { 500 };
|
||||
RefPtr<Core::Timer> m_timer;
|
||||
};
|
11
Userland/Games/GameOfLife/CMakeLists.txt
Normal file
11
Userland/Games/GameOfLife/CMakeLists.txt
Normal file
|
@ -0,0 +1,11 @@
|
|||
compile_gml(GameOfLife.gml GameOfLifeGML.h game_of_life_gml)
|
||||
|
||||
set(SOURCES
|
||||
Board.cpp
|
||||
BoardWidget.cpp
|
||||
GameOfLifeGML.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_app(GameOfLife ICON app-gameoflife)
|
||||
target_link_libraries(GameOfLife LibGUI)
|
57
Userland/Games/GameOfLife/GameOfLife.gml
Normal file
57
Userland/Games/GameOfLife/GameOfLife.gml
Normal file
|
@ -0,0 +1,57 @@
|
|||
@GUI::Widget {
|
||||
layout: @GUI::VerticalBoxLayout {
|
||||
}
|
||||
|
||||
@GUI::ToolbarContainer {
|
||||
|
||||
@GUI::Toolbar {
|
||||
name: "toolbar"
|
||||
|
||||
@GUI::Label {
|
||||
text: "Columns:"
|
||||
fixed_width: 60
|
||||
}
|
||||
|
||||
@GUI::SpinBox {
|
||||
name: "columns_spinbox"
|
||||
min: 10
|
||||
max: 999
|
||||
fixed_width: 40
|
||||
}
|
||||
@GUI::VerticalSeparator {
|
||||
}
|
||||
@GUI::Label {
|
||||
text: "Rows:"
|
||||
fixed_width: 40
|
||||
}
|
||||
|
||||
@GUI::SpinBox {
|
||||
name: "rows_spinbox"
|
||||
min: 10
|
||||
max: 999
|
||||
fixed_width: 40
|
||||
}
|
||||
@GUI::VerticalSeparator {
|
||||
}
|
||||
@GUI::Label {
|
||||
text: "Update Speed:"
|
||||
fixed_width: 90
|
||||
}
|
||||
@GUI::SpinBox {
|
||||
name: "interval_spinbox"
|
||||
min: 10
|
||||
max: 5000
|
||||
fixed_width: 60
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GUI::Widget {
|
||||
name: "board_widget_container"
|
||||
fill_with_background_color: true
|
||||
}
|
||||
|
||||
@GUI::Statusbar {
|
||||
name: "statusbar"
|
||||
}
|
||||
}
|
170
Userland/Games/GameOfLife/main.cpp
Normal file
170
Userland/Games/GameOfLife/main.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Andres Crucitti <dasc495@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "BoardWidget.h"
|
||||
#include <Games/GameOfLife/GameOfLifeGML.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Button.h>
|
||||
#include <LibGUI/Icon.h>
|
||||
#include <LibGUI/Label.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Menubar.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
#include <LibGUI/Slider.h>
|
||||
#include <LibGUI/SpinBox.h>
|
||||
#include <LibGUI/Statusbar.h>
|
||||
#include <LibGUI/Toolbar.h>
|
||||
#include <LibGUI/ToolbarContainer.h>
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
const char* click_tip = "Tip: click the board to toggle individual cells, or click+drag to toggle multiple cells";
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto app = GUI::Application::construct(argc, argv);
|
||||
auto app_icon = GUI::Icon::default_icon("app-gameoflife");
|
||||
|
||||
auto window = GUI::Window::construct();
|
||||
window->set_icon(app_icon.bitmap_for_size(16));
|
||||
|
||||
size_t board_columns = 35;
|
||||
size_t board_rows = 35;
|
||||
|
||||
window->set_double_buffering_enabled(false);
|
||||
window->set_title("Game Of Life");
|
||||
|
||||
auto& main_widget = window->set_main_widget<GUI::Widget>();
|
||||
main_widget.load_from_gml(game_of_life_gml);
|
||||
main_widget.set_fill_with_background_color(true);
|
||||
|
||||
auto& main_toolbar = *main_widget.find_descendant_of_type_named<GUI::Toolbar>("toolbar");
|
||||
|
||||
auto& board_widget_container = *main_widget.find_descendant_of_type_named<GUI::Widget>("board_widget_container");
|
||||
auto& board_layout = board_widget_container.set_layout<GUI::VerticalBoxLayout>();
|
||||
board_layout.set_spacing(0);
|
||||
auto& board_widget = board_widget_container.add<BoardWidget>(board_rows, board_columns);
|
||||
board_widget.randomize_cells();
|
||||
|
||||
auto& statusbar = *main_widget.find_descendant_of_type_named<GUI::Statusbar>("statusbar");
|
||||
statusbar.set_text(click_tip);
|
||||
|
||||
auto& columns_spinbox = *main_widget.find_descendant_of_type_named<GUI::SpinBox>("columns_spinbox");
|
||||
auto& rows_spinbox = *main_widget.find_descendant_of_type_named<GUI::SpinBox>("rows_spinbox");
|
||||
|
||||
columns_spinbox.set_value(board_columns);
|
||||
rows_spinbox.set_value(board_rows);
|
||||
|
||||
auto size_changed_function = [&] {
|
||||
statusbar.set_text(click_tip);
|
||||
board_widget.update_board(rows_spinbox.value(), columns_spinbox.value());
|
||||
board_widget.randomize_cells();
|
||||
board_widget.update();
|
||||
};
|
||||
|
||||
rows_spinbox.on_change = [&](auto) { size_changed_function(); };
|
||||
columns_spinbox.on_change = [&](auto) { size_changed_function(); };
|
||||
|
||||
auto& interval_spinbox = *main_widget.find_descendant_of_type_named<GUI::SpinBox>("interval_spinbox");
|
||||
|
||||
interval_spinbox.on_change = [&](auto value) {
|
||||
board_widget.set_running_timer_interval(value);
|
||||
};
|
||||
|
||||
interval_spinbox.set_value(150);
|
||||
|
||||
auto interval_label = GUI::Label::construct();
|
||||
interval_label->set_fixed_width(15);
|
||||
interval_label->set_text("ms");
|
||||
|
||||
main_toolbar.add_child(interval_label);
|
||||
|
||||
auto paused_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png");
|
||||
auto play_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png");
|
||||
|
||||
auto toggle_running_action = GUI::Action::create("Toggle Running", { Mod_None, Key_Return }, *play_icon, [&](GUI::Action&) {
|
||||
board_widget.set_running(!board_widget.is_running());
|
||||
});
|
||||
|
||||
toggle_running_action->set_checkable(true);
|
||||
main_toolbar.add_action(toggle_running_action);
|
||||
|
||||
auto run_one_generation_action = GUI::Action::create("Run Next Generation", { Mod_Ctrl, Key_Equal }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [&](const GUI::Action&) {
|
||||
statusbar.set_text(click_tip);
|
||||
board_widget.run_generation();
|
||||
});
|
||||
main_toolbar.add_action(run_one_generation_action);
|
||||
|
||||
auto clear_board_action = GUI::Action::create("Clear board", { Mod_Ctrl, Key_N }, Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"), [&](auto&) {
|
||||
statusbar.set_text(click_tip);
|
||||
board_widget.clear_cells();
|
||||
board_widget.update();
|
||||
});
|
||||
main_toolbar.add_action(clear_board_action);
|
||||
|
||||
auto randomize_cells_action = GUI::Action::create("Randomize board", { Mod_Ctrl, Key_R }, Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"), [&](auto&) {
|
||||
statusbar.set_text(click_tip);
|
||||
board_widget.randomize_cells();
|
||||
board_widget.update();
|
||||
});
|
||||
main_toolbar.add_action(randomize_cells_action);
|
||||
|
||||
auto menubar = GUI::Menubar::construct();
|
||||
auto& app_menu = menubar->add_menu("Game Of Life");
|
||||
|
||||
app_menu.add_action(clear_board_action);
|
||||
app_menu.add_action(randomize_cells_action);
|
||||
app_menu.add_separator();
|
||||
app_menu.add_action(toggle_running_action);
|
||||
app_menu.add_action(run_one_generation_action);
|
||||
|
||||
app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
||||
GUI::Application::the()->quit();
|
||||
}));
|
||||
|
||||
auto& help_menu = menubar->add_menu("Help");
|
||||
help_menu.add_action(GUI::CommonActions::make_about_action("GameOfLife", app_icon, window));
|
||||
|
||||
window->set_menubar(move(menubar));
|
||||
|
||||
board_widget.on_running_state_change = [&]() {
|
||||
if (board_widget.is_running()) {
|
||||
statusbar.set_text("Running...");
|
||||
toggle_running_action->set_icon(paused_icon);
|
||||
main_widget.set_override_cursor(Gfx::StandardCursor::None);
|
||||
} else {
|
||||
statusbar.set_text(click_tip);
|
||||
toggle_running_action->set_icon(play_icon);
|
||||
main_widget.set_override_cursor(Gfx::StandardCursor::Drag);
|
||||
}
|
||||
|
||||
interval_spinbox.set_value(board_widget.running_timer_interval());
|
||||
|
||||
rows_spinbox.set_enabled(!board_widget.is_running());
|
||||
columns_spinbox.set_enabled(!board_widget.is_running());
|
||||
interval_spinbox.set_enabled(!board_widget.is_running());
|
||||
|
||||
run_one_generation_action->set_enabled(!board_widget.is_running());
|
||||
clear_board_action->set_enabled(!board_widget.is_running());
|
||||
randomize_cells_action->set_enabled(!board_widget.is_running());
|
||||
|
||||
board_widget.update();
|
||||
};
|
||||
|
||||
board_widget.on_stall = [&] {
|
||||
toggle_running_action->activate();
|
||||
statusbar.set_text("Stalled...");
|
||||
};
|
||||
|
||||
board_widget.on_cell_toggled = [&](auto, auto) {
|
||||
statusbar.set_text(click_tip);
|
||||
};
|
||||
|
||||
window->resize(500, 420);
|
||||
window->show();
|
||||
|
||||
return app->exec();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue