mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 10:27:35 +00:00
MasterWord: Display messages in a statusbar
In the "inspiration" for this game, messages are displayed on top of the game area in case an invalid guess is inputted. After a few seconds, they disappear. In a similar fashion, a statusbar is created on the game window and similar messages are outputted there.
This commit is contained in:
parent
d3588a9a2b
commit
cc5ea3aa4c
5 changed files with 94 additions and 19 deletions
|
@ -4,10 +4,16 @@ serenity_component(
|
||||||
TARGETS MasterWord
|
TARGETS MasterWord
|
||||||
)
|
)
|
||||||
|
|
||||||
|
compile_gml(MasterWord.gml MasterWordGML.h master_word_gml)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
main.cpp
|
main.cpp
|
||||||
WordGame.cpp
|
WordGame.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(GENERATED_SOURCES
|
||||||
|
MasterWordGML.h
|
||||||
|
)
|
||||||
|
|
||||||
serenity_app(MasterWord ICON app-masterword)
|
serenity_app(MasterWord ICON app-masterword)
|
||||||
target_link_libraries(MasterWord PRIVATE LibCore LibGfx LibGUI LibConfig LibMain LibDesktop)
|
target_link_libraries(MasterWord PRIVATE LibCore LibGfx LibGUI LibConfig LibMain LibDesktop)
|
||||||
|
|
13
Userland/Games/MasterWord/MasterWord.gml
Normal file
13
Userland/Games/MasterWord/MasterWord.gml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
@GUI::Widget {
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
spacing: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@MasterWord::WordGame {
|
||||||
|
name: "word_game"
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::Statusbar {
|
||||||
|
name: "statusbar"
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,18 +7,25 @@
|
||||||
#include "WordGame.h"
|
#include "WordGame.h"
|
||||||
#include <AK/QuickSort.h>
|
#include <AK/QuickSort.h>
|
||||||
#include <AK/Random.h>
|
#include <AK/Random.h>
|
||||||
|
#include <AK/StringView.h>
|
||||||
#include <LibConfig/Client.h>
|
#include <LibConfig/Client.h>
|
||||||
#include <LibCore/Stream.h>
|
#include <LibCore/Stream.h>
|
||||||
|
#include <LibCore/Timer.h>
|
||||||
#include <LibGUI/Application.h>
|
#include <LibGUI/Application.h>
|
||||||
#include <LibGUI/MessageBox.h>
|
#include <LibGUI/MessageBox.h>
|
||||||
#include <LibGUI/Painter.h>
|
#include <LibGUI/Painter.h>
|
||||||
|
#include <LibGUI/Widget.h>
|
||||||
#include <LibGfx/Font/Font.h>
|
#include <LibGfx/Font/Font.h>
|
||||||
#include <LibGfx/Font/FontDatabase.h>
|
#include <LibGfx/Font/FontDatabase.h>
|
||||||
#include <LibGfx/Palette.h>
|
#include <LibGfx/Palette.h>
|
||||||
|
|
||||||
|
REGISTER_WIDGET(MasterWord, WordGame)
|
||||||
|
|
||||||
// TODO: Add stats
|
// TODO: Add stats
|
||||||
|
namespace MasterWord {
|
||||||
|
|
||||||
WordGame::WordGame()
|
WordGame::WordGame()
|
||||||
|
: m_clear_message_timer(Core::Timer::create_single_shot(5000, [this] { clear_message(); }))
|
||||||
{
|
{
|
||||||
read_words();
|
read_words();
|
||||||
m_num_letters = Config::read_i32("MasterWord"sv, ""sv, "word_length"sv, 5);
|
m_num_letters = Config::read_i32("MasterWord"sv, ""sv, "word_length"sv, 5);
|
||||||
|
@ -42,6 +49,7 @@ void WordGame::reset()
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
clear_message();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,10 +90,17 @@ void WordGame::keydown_event(GUI::KeyEvent& event)
|
||||||
m_current_guess = m_current_guess.substring(0, m_current_guess.length() - 1);
|
m_current_guess = m_current_guess.substring(0, m_current_guess.length() - 1);
|
||||||
m_last_word_not_in_dictionary = false;
|
m_last_word_not_in_dictionary = false;
|
||||||
}
|
}
|
||||||
// If enough letters and return pressed
|
// If return pressed
|
||||||
else if (m_current_guess.length() == m_num_letters && event.key() == KeyCode::Key_Return) {
|
else if (event.key() == KeyCode::Key_Return) {
|
||||||
if (is_in_dictionary(m_current_guess)) {
|
if (m_current_guess.length() < m_num_letters) {
|
||||||
|
show_message("Not enough letters"sv);
|
||||||
|
} else if (!is_in_dictionary(m_current_guess)) {
|
||||||
|
show_message("Not in dictionary"sv);
|
||||||
|
m_last_word_not_in_dictionary = true;
|
||||||
|
} else {
|
||||||
m_last_word_not_in_dictionary = false;
|
m_last_word_not_in_dictionary = false;
|
||||||
|
clear_message();
|
||||||
|
|
||||||
add_guess(m_current_guess);
|
add_guess(m_current_guess);
|
||||||
auto won = m_current_guess == m_current_word;
|
auto won = m_current_guess == m_current_word;
|
||||||
m_current_guess = {};
|
m_current_guess = {};
|
||||||
|
@ -96,8 +111,6 @@ void WordGame::keydown_event(GUI::KeyEvent& event)
|
||||||
GUI::MessageBox::show(window(), DeprecatedString::formatted("You lose!\nThe word was {}", m_current_word), "MasterWord"sv);
|
GUI::MessageBox::show(window(), DeprecatedString::formatted("You lose!\nThe word was {}", m_current_word), "MasterWord"sv);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
m_last_word_not_in_dictionary = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,3 +304,19 @@ void WordGame::add_guess(AK::StringView guess)
|
||||||
m_guesses.append({ guess, letter_states });
|
m_guesses.append({ guess, letter_states });
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WordGame::show_message(StringView message) const
|
||||||
|
{
|
||||||
|
m_clear_message_timer->restart();
|
||||||
|
if (on_message)
|
||||||
|
on_message(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WordGame::clear_message() const
|
||||||
|
{
|
||||||
|
m_clear_message_timer->stop();
|
||||||
|
if (on_message)
|
||||||
|
on_message({});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -7,10 +7,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/DeprecatedString.h>
|
#include <AK/DeprecatedString.h>
|
||||||
|
#include <AK/Forward.h>
|
||||||
|
#include <AK/RefPtr.h>
|
||||||
|
#include <AK/StringView.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibCore/Timer.h>
|
||||||
#include <LibGUI/Frame.h>
|
#include <LibGUI/Frame.h>
|
||||||
#include <LibGfx/Rect.h>
|
#include <LibGfx/Rect.h>
|
||||||
|
|
||||||
|
namespace MasterWord {
|
||||||
|
|
||||||
class WordGame : public GUI::Frame {
|
class WordGame : public GUI::Frame {
|
||||||
C_OBJECT(WordGame);
|
C_OBJECT(WordGame);
|
||||||
|
|
||||||
|
@ -32,9 +38,13 @@ public:
|
||||||
void add_guess(AK::StringView guess);
|
void add_guess(AK::StringView guess);
|
||||||
bool is_in_dictionary(AK::StringView guess);
|
bool is_in_dictionary(AK::StringView guess);
|
||||||
|
|
||||||
|
Function<void(Optional<StringView>)> on_message;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WordGame();
|
WordGame();
|
||||||
void read_words();
|
void read_words();
|
||||||
|
void show_message(StringView message) const;
|
||||||
|
void clear_message() const;
|
||||||
|
|
||||||
virtual void paint_event(GUI::PaintEvent&) override;
|
virtual void paint_event(GUI::PaintEvent&) override;
|
||||||
virtual void keydown_event(GUI::KeyEvent&) override;
|
virtual void keydown_event(GUI::KeyEvent&) override;
|
||||||
|
@ -76,4 +86,8 @@ private:
|
||||||
AK::DeprecatedString m_current_word;
|
AK::DeprecatedString m_current_word;
|
||||||
|
|
||||||
HashMap<size_t, AK::Vector<DeprecatedString>> m_words;
|
HashMap<size_t, AK::Vector<DeprecatedString>> m_words;
|
||||||
|
|
||||||
|
NonnullRefPtr<Core::Timer> m_clear_message_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "WordGame.h"
|
#include "WordGame.h"
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
|
#include <Games/MasterWord/MasterWordGML.h>
|
||||||
#include <LibConfig/Client.h>
|
#include <LibConfig/Client.h>
|
||||||
#include <LibCore/System.h>
|
#include <LibCore/System.h>
|
||||||
#include <LibDesktop/Launcher.h>
|
#include <LibDesktop/Launcher.h>
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
#include <LibGUI/InputBox.h>
|
#include <LibGUI/InputBox.h>
|
||||||
#include <LibGUI/Menubar.h>
|
#include <LibGUI/Menubar.h>
|
||||||
#include <LibGUI/MessageBox.h>
|
#include <LibGUI/MessageBox.h>
|
||||||
|
#include <LibGUI/Statusbar.h>
|
||||||
#include <LibGUI/Window.h>
|
#include <LibGUI/Window.h>
|
||||||
#include <LibMain/Main.h>
|
#include <LibMain/Main.h>
|
||||||
|
|
||||||
|
@ -44,20 +46,24 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
window->set_title("MasterWord");
|
window->set_title("MasterWord");
|
||||||
window->set_resizable(false);
|
window->set_resizable(false);
|
||||||
|
|
||||||
auto game = TRY(window->try_set_main_widget<WordGame>());
|
auto main_widget = TRY(window->try_set_main_widget<GUI::Widget>());
|
||||||
|
main_widget->load_from_gml(master_word_gml);
|
||||||
|
auto& game = *main_widget->find_descendant_of_type_named<MasterWord::WordGame>("word_game");
|
||||||
|
auto& statusbar = *main_widget->find_descendant_of_type_named<GUI::Statusbar>("statusbar");
|
||||||
|
|
||||||
auto use_system_theme = Config::read_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false);
|
auto use_system_theme = Config::read_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false);
|
||||||
game->set_use_system_theme(use_system_theme);
|
game.set_use_system_theme(use_system_theme);
|
||||||
|
|
||||||
auto shortest_word = game->shortest_word();
|
auto shortest_word = game.shortest_word();
|
||||||
auto longest_word = game->longest_word();
|
auto longest_word = game.longest_word();
|
||||||
|
|
||||||
window->resize(game->game_size());
|
window->resize(game.game_size());
|
||||||
|
window->set_focused_widget(&game);
|
||||||
|
|
||||||
auto game_menu = TRY(window->try_add_menu("&Game"));
|
auto game_menu = TRY(window->try_add_menu("&Game"));
|
||||||
|
|
||||||
TRY(game_menu->try_add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, [&](auto&) {
|
TRY(game_menu->try_add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, [&](auto&) {
|
||||||
game->reset();
|
game.reset();
|
||||||
})));
|
})));
|
||||||
|
|
||||||
TRY(game_menu->try_add_separator());
|
TRY(game_menu->try_add_separator());
|
||||||
|
@ -79,8 +85,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
word_length = maybe_word_length.value();
|
word_length = maybe_word_length.value();
|
||||||
Config::write_i32("MasterWord"sv, ""sv, "word_length"sv, word_length);
|
Config::write_i32("MasterWord"sv, ""sv, "word_length"sv, word_length);
|
||||||
game->set_word_length(word_length);
|
game.set_word_length(word_length);
|
||||||
window->resize(game->game_size());
|
window->resize(game.game_size());
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
TRY(settings_menu->try_add_action(GUI::Action::create("Set &Number Of Guesses", [&](auto&) {
|
TRY(settings_menu->try_add_action(GUI::Action::create("Set &Number Of Guesses", [&](auto&) {
|
||||||
|
@ -95,22 +101,22 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
max_guesses = maybe_max_guesses.value();
|
max_guesses = maybe_max_guesses.value();
|
||||||
Config::write_i32("MasterWord"sv, ""sv, "max_guesses"sv, max_guesses);
|
Config::write_i32("MasterWord"sv, ""sv, "max_guesses"sv, max_guesses);
|
||||||
game->set_max_guesses(max_guesses);
|
game.set_max_guesses(max_guesses);
|
||||||
window->resize(game->game_size());
|
window->resize(game.game_size());
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
|
||||||
auto toggle_check_guesses = GUI::Action::create_checkable("Check &Guesses in dictionary", [&](auto& action) {
|
auto toggle_check_guesses = GUI::Action::create_checkable("Check &Guesses in dictionary", [&](auto& action) {
|
||||||
auto checked = action.is_checked();
|
auto checked = action.is_checked();
|
||||||
game->set_check_guesses_in_dictionary(checked);
|
game.set_check_guesses_in_dictionary(checked);
|
||||||
Config::write_bool("MasterWord"sv, ""sv, "check_guesses_in_dictionary"sv, checked);
|
Config::write_bool("MasterWord"sv, ""sv, "check_guesses_in_dictionary"sv, checked);
|
||||||
});
|
});
|
||||||
toggle_check_guesses->set_checked(game->is_checking_guesses());
|
toggle_check_guesses->set_checked(game.is_checking_guesses());
|
||||||
TRY(settings_menu->try_add_action(toggle_check_guesses));
|
TRY(settings_menu->try_add_action(toggle_check_guesses));
|
||||||
|
|
||||||
auto theme_menu = TRY(window->try_add_menu("&Theme"));
|
auto theme_menu = TRY(window->try_add_menu("&Theme"));
|
||||||
auto system_theme_action = GUI::Action::create("&System", [&](auto&) {
|
auto system_theme_action = GUI::Action::create("&System", [&](auto&) {
|
||||||
game->set_use_system_theme(true);
|
game.set_use_system_theme(true);
|
||||||
Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, true);
|
Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, true);
|
||||||
});
|
});
|
||||||
system_theme_action->set_checkable(true);
|
system_theme_action->set_checkable(true);
|
||||||
|
@ -118,7 +124,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
TRY(theme_menu->try_add_action(system_theme_action));
|
TRY(theme_menu->try_add_action(system_theme_action));
|
||||||
|
|
||||||
auto wordle_theme_action = GUI::Action::create("&Wordle", [&](auto&) {
|
auto wordle_theme_action = GUI::Action::create("&Wordle", [&](auto&) {
|
||||||
game->set_use_system_theme(false);
|
game.set_use_system_theme(false);
|
||||||
Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false);
|
Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false);
|
||||||
});
|
});
|
||||||
wordle_theme_action->set_checkable(true);
|
wordle_theme_action->set_checkable(true);
|
||||||
|
@ -138,6 +144,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
})));
|
})));
|
||||||
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("MasterWord", app_icon, window)));
|
TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("MasterWord", app_icon, window)));
|
||||||
|
|
||||||
|
game.on_message = [&](auto message) {
|
||||||
|
if (!message.has_value())
|
||||||
|
statusbar.set_text("");
|
||||||
|
else
|
||||||
|
statusbar.set_text(*message);
|
||||||
|
};
|
||||||
|
|
||||||
window->show();
|
window->show();
|
||||||
|
|
||||||
window->set_icon(app_icon.bitmap_for_size(16));
|
window->set_icon(app_icon.bitmap_for_size(16));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue