Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display world screenshot previews and progress #2349

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
19b45a5
Display world screenshot previews and progress
Vankata453 Dec 4, 2022
058899e
Move preview functionality to menus and menu items
Vankata453 Dec 12, 2022
692d5a0
Merge branch 'master' into world-previews
Vankata453 Apr 29, 2023
c38af90
FileSystemMenu now shows previews of images
Vankata453 Apr 29, 2023
78094b3
Cast integers to floats when generating preview rect
Vankata453 Apr 29, 2023
24dc82f
Cast world entries vector size to int
Vankata453 Apr 29, 2023
5cd4f74
Merge remote-tracking branch 'supertux/master' into world-previews
Vankata453 May 20, 2023
9be8c3d
Don't fade in/out previews when transitions are disabled
Vankata453 May 20, 2023
bbd65fe
Merge branch 'master' into world-previews
Vankata453 May 24, 2023
9a9d115
Merge remote-tracking branch 'supertux/master' into world-previews
Vankata453 Jul 27, 2023
c0ba8e5
Remove some header includes
Vankata453 Jul 27, 2023
3c5ecec
Show progress percentage for worlds
Vankata453 Aug 2, 2023
7cdf956
General code improvements
Vankata453 Aug 5, 2023
062c452
Get level progress from savefile level tables
Vankata453 Aug 5, 2023
827d922
Allow toggling world previews functionality
Vankata453 Aug 5, 2023
1149313
Use unsigned integers for storing progress
Vankata453 Aug 6, 2023
8a23ab0
Add `<sstream>` include
Vankata453 Aug 6, 2023
91ea207
Do not draw menu item previews, if preview overlaps the menu
Vankata453 Aug 6, 2023
17e2259
`WorldMapState` now loads/saves state for all sectors
Vankata453 Aug 6, 2023
08efd94
Merge branch 'master' into world-previews
Vankata453 Oct 27, 2023
3c421d5
Fix macro `;` warning
Vankata453 Nov 18, 2023
41bed28
Merge remote-tracking branch 'supertux/master' into world-previews
Vankata453 Jul 13, 2024
492395c
Fix issues displaying world previews
Vankata453 Jul 14, 2024
78df407
Code fixes [ci skip]
Vankata453 Jul 14, 2024
d8a7c73
Align menu help text to screen center
Vankata453 Jul 15, 2024
9612857
Menu item image preview size is now relative to screen size
Vankata453 Jul 15, 2024
610a448
Calculate world progress percentage using perfect level states
Vankata453 Jul 16, 2024
8763b68
Draw white border around previews
Vankata453 Jul 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 107 additions & 2 deletions src/gui/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,18 @@
#include "gui/menu_manager.hpp"
#include "gui/mousecursor.hpp"
#include "math/util.hpp"
#include "supertux/error_handler.hpp"
#include "supertux/gameconfig.hpp"
#include "supertux/globals.hpp"
#include "supertux/resources.hpp"
#include "video/drawing_context.hpp"
#include "video/renderer.hpp"
#include "video/surface.hpp"
#include "video/video_system.hpp"
#include "video/viewport.hpp"

#include "supertux/error_handler.hpp"
const Sizef Menu::s_preview_size(426.f, 240.f);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding the preview size squishes pictures that are too big. Any reason as to why you're doing this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the image and the menu could both fit on the screen. Maybe I should check the resolution before determining this though.

const float Menu::s_preview_fade_time = 0.1f;

Menu::Menu() :
m_pos(Vector(static_cast<float>(SCREEN_WIDTH) / 2.0f,
Expand All @@ -64,8 +67,14 @@ Menu::Menu() :
m_menu_help_height(0.0f),
m_items(),
m_arrange_left(0),
m_active_item(-1)
m_active_item(-1),
m_has_previews(false),
m_last_preview_item(-1),
m_preview_fade_timer(),
m_preview_fade_active(false),
m_preview_fading_out(false)
{
m_preview_fade_timer.start(g_config->transitions_enabled ? s_preview_fade_time : 0);
}

Menu::~Menu()
Expand All @@ -79,6 +88,23 @@ Menu::set_center_pos(float x, float y)
m_pos.y = y;
}

void
Menu::align_for_previews(float x_offset)
{
for (const auto& item : m_items)
{
if (item->get_preview())
{
// Adjust center position to give space for displaying previews.
set_center_pos(static_cast<float>(SCREEN_WIDTH) / 2 - get_width() / 2 - x_offset,
static_cast<float>(SCREEN_HEIGHT) / 2);
m_has_previews = true;
return;
}
}
m_has_previews = false;
}

/* Add an item to a menu */
MenuItem&
Menu::add_item(std::unique_ptr<MenuItem> new_item)
Expand Down Expand Up @@ -578,6 +604,7 @@ Menu::on_window_resize()

calculate_width();
calculate_height();
align_for_previews();

for (auto& item : m_items)
item->on_window_resize();
Expand Down Expand Up @@ -620,6 +647,84 @@ Menu::draw(DrawingContext& context)
Vector(m_pos.x, static_cast<float>(SCREEN_HEIGHT) - 48.0f - static_cast<float>(text_height) / 2.0f),
ALIGN_CENTER, LAYER_GUI);
}

if (m_has_previews) draw_preview(context);
}

void
Menu::draw_preview(DrawingContext& context)
{
bool valid_last_index = last_preview_index_valid();

// Update fade.
if (m_active_item != m_last_preview_item && !m_preview_fade_active) // Index has changed, there is no current fade.
{
if (valid_last_index) // Fade out only if the last index is valid.
m_preview_fade_timer.start(g_config->transitions_enabled ? s_preview_fade_time : 0.f);
m_preview_fading_out = true;
m_preview_fade_active = true;
}
float timeleft = m_preview_fade_timer.get_timeleft();
if (timeleft < 0 && m_preview_fade_active) // Current fade is over.
{
m_last_preview_item = m_active_item;
valid_last_index = last_preview_index_valid(); // Repeat valid last index check
if (m_preview_fading_out) // After a fade-out, a fade-in should follow up.
{
m_preview_fade_timer.start(g_config->transitions_enabled ? s_preview_fade_time : 0.f);
timeleft = m_preview_fade_timer.get_timeleft();
m_preview_fading_out = false;
}
else
{
m_preview_fade_active = false;
}
}

// Set alpha according to fade.
float alpha = 1.f;
if (timeleft > 0)
{
const float alpha_val = timeleft * (1.f / s_preview_fade_time);
alpha = m_preview_fading_out ? alpha_val : 1.f - alpha_val;
}

// Perform actions only if current index is a valid preview index.
if (valid_last_index)
{
// Draw progress preview of current item.
SurfacePtr preview = m_items[m_last_preview_item]->get_preview();
const float width_diff = s_preview_size.width - static_cast<float>(preview->get_width());
const float height_diff = s_preview_size.height - static_cast<float>(preview->get_height());
// If the preview is smaller than the maximal size, make sure to draw it with its original size and adjust position to center.
Rectf preview_rect(Vector(static_cast<float>(context.get_width()) * 0.73f - s_preview_size.width / 2 + (width_diff > 0 ? width_diff / 2 : 0),
static_cast<float>(context.get_height()) / 2 - s_preview_size.height / 2 + (height_diff > 0 ? height_diff / 2 : 0)),
Sizef(width_diff > 0 ? static_cast<float>(preview->get_width()) : s_preview_size.width,
height_diff > 0 ? static_cast<float>(preview->get_height()) : s_preview_size.height));

// If the preview starts overlapping the menu, due to a smaller screen resolution, do not draw it.
// Instead, set the Y position to half the height, so preview data, if available, can still be drawn.
if (preview_rect.get_left() <= m_pos.x + m_menu_width / 2)
{
preview_rect.set_top(preview_rect.get_top() + preview_rect.get_height() / 2);
preview_rect.set_height(0.f);
}
else
{
PaintStyle style;
style.set_alpha(alpha);
context.color().draw_surface_scaled(preview, preview_rect, LAYER_GUI, style);
}

// Draw other data, alongside the preview, if available.
draw_preview_data(context, preview_rect, alpha);
}
}

bool
Menu::last_preview_index_valid() const
{
return m_last_preview_item > -1 && m_items[m_last_preview_item]->get_preview();
}

MenuItem&
Expand Down
22 changes: 22 additions & 0 deletions src/gui/menu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@
#include <SDL.h>

#include "gui/menu_action.hpp"
#include "math/rectf.hpp"
#include "math/sizef.hpp"
#include "math/vector.hpp"
#include "supertux/timer.hpp"
#include "video/color.hpp"
#include "video/drawing_context.hpp"

Expand Down Expand Up @@ -54,6 +57,10 @@ class PathObject;

class Menu
{
protected:
static const Sizef s_preview_size;
static const float s_preview_fade_time;

public:
Menu();
virtual ~Menu();
Expand Down Expand Up @@ -113,6 +120,9 @@ class Menu
/** Remove all entries from the menu */
void clear();

/** Align the menu to the left side, if any previews are available. */
void align_for_previews(float x_offset = 30.f);

MenuItem& get_item(int index) { return *(m_items[index]); }

MenuItem& get_item_by_id(int id);
Expand Down Expand Up @@ -140,9 +150,14 @@ class Menu
/** Recalculates the height for this menu */
void calculate_height();

/** Draw additional data to accompany item previews. */
virtual void draw_preview_data(DrawingContext& context, const Rectf& preview_rect, float alpha) {}

private:
void check_controlfield_change_event(const SDL_Event& event);
void draw_item(DrawingContext& context, int index, float y_pos);
void draw_preview(DrawingContext& context);
bool last_preview_index_valid() const;

private:
/** position of the menu (ie. center of the menu, not top/left) */
Expand All @@ -164,6 +179,13 @@ class Menu
protected:
int m_active_item;

/* Preview implementation variables. */
bool m_has_previews;
int m_last_preview_item;
Timer m_preview_fade_timer;
bool m_preview_fade_active;
bool m_preview_fading_out;

private:
Menu(const Menu&) = delete;
Menu& operator=(const Menu&) = delete;
Expand Down
31 changes: 26 additions & 5 deletions src/gui/menu_filesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include "util/gettext.hpp"
#include "util/string_util.hpp"

const size_t FileSystemMenu::s_title_max_chars = 30;
const std::vector<std::string> FileSystemMenu::s_image_extensions = { ".jpg", ".png", ".surface" };

FileSystemMenu::FileSystemMenu(std::string* filename, const std::vector<std::string>& extensions,
const std::string& basedir, bool path_relative_to_basedir, std::function<void(std::string)> callback,
const std::function<void (MenuItem&)>& item_processor) :
Expand Down Expand Up @@ -65,7 +68,13 @@ FileSystemMenu::refresh_items()
m_files.clear();
m_directory = FileSystem::normalize(m_directory);

add_label(m_directory);
// Make sure label doesn't get too long.
std::string title = m_directory;
const bool title_large = title.size() > s_title_max_chars;
while (title.size() > s_title_max_chars) title = title.substr(title.size() - s_title_max_chars);
if (title_large) title = "..." + title;

add_label(title);
add_hl();

int item_id = 0;
Expand Down Expand Up @@ -108,8 +117,11 @@ FileSystemMenu::refresh_items()
for (const auto& item : m_files)
{
MenuItem& menu_item = add_entry(item_id, item);

if (in_basedir && m_item_processor)
m_item_processor(menu_item);
if (is_image(item))
menu_item.set_preview(FileSystem::join(m_directory, item));

item_id++;
}
Expand All @@ -123,6 +135,7 @@ FileSystemMenu::refresh_items()

// Re-center menu
on_window_resize();
align_for_previews(25.f);
}

bool
Expand All @@ -131,12 +144,20 @@ FileSystemMenu::has_right_suffix(const std::string& file) const
if (m_extensions.empty())
return true;

for (const auto& extension : m_extensions) {
for (const auto& extension : m_extensions)
if (StringUtil::has_suffix(file, extension))
{
return true;
}
}

return false;
}

bool
FileSystemMenu::is_image(const std::string& file) const
{
for (const auto& extension : s_image_extensions)
if (StringUtil::has_suffix(file, extension))
return true;

return false;
}

Expand Down
5 changes: 5 additions & 0 deletions src/gui/menu_filesystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@

class FileSystemMenu final : public Menu
{
private:
static const size_t s_title_max_chars;
static const std::vector<std::string> s_image_extensions;

public:
FileSystemMenu(std::string* filename, const std::vector<std::string>& extensions,
const std::string& basedir, bool path_relative_to_basedir, const std::function<void(std::string)> callback = nullptr,
Expand All @@ -32,6 +36,7 @@ class FileSystemMenu final : public Menu
private:
void refresh_items();
bool has_right_suffix(const std::string& file) const;
bool is_image(const std::string& file) const;

private:
std::string* m_filename;
Expand Down
10 changes: 9 additions & 1 deletion src/gui/menu_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@
#include "supertux/globals.hpp"
#include "supertux/resources.hpp"
#include "video/drawing_context.hpp"
#include "video/surface.hpp"

//static const float FLICK_CURSOR_TIME = 0.5f;

MenuItem::MenuItem(const std::string& text, int id) :
m_id(id),
m_text(text),
m_help(),
m_font(Resources::normal_font)
m_font(Resources::normal_font),
m_preview()
{
}

Expand All @@ -49,6 +51,12 @@ MenuItem::set_help(const std::string& help_text)
}
}

void
MenuItem::set_preview(const std::string& preview_file)
{
m_preview = Surface::from_file(preview_file);
}

void
MenuItem::draw(DrawingContext& context, const Vector& pos, int menu_width, bool active)
{
Expand Down
7 changes: 7 additions & 0 deletions src/gui/menu_item.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "gui/menu.hpp"

#include "video/surface_ptr.hpp"

class MenuItem
{
public:
Expand All @@ -37,6 +39,10 @@ class MenuItem
void set_font(const FontPtr font) { m_font = font; }
const FontPtr& get_font() const { return m_font; }

void set_preview(const std::string& preview_file);
void set_preview(SurfacePtr preview) { m_preview = preview; }
SurfacePtr get_preview() const { return m_preview; }

/** Draws the menu item. */
virtual void draw(DrawingContext&, const Vector& pos, int menu_width, bool active);

Expand Down Expand Up @@ -86,6 +92,7 @@ class MenuItem
std::string m_text;
std::string m_help;
FontPtr m_font;
SurfacePtr m_preview;

private:
MenuItem(const MenuItem&) = delete;
Expand Down
3 changes: 3 additions & 0 deletions src/supertux/gameconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ Config::Config() :
#else
do_release_check(true),
#endif
show_world_previews(true),
custom_title_levels(true),
#ifdef ENABLE_DISCORD
enable_discord(false),
Expand Down Expand Up @@ -155,6 +156,7 @@ Config::load()
config_mapping.get("pause_on_focusloss", pause_on_focusloss);
config_mapping.get("custom_mouse_cursor", custom_mouse_cursor);
config_mapping.get("do_release_check", do_release_check);
config_mapping.get("show_world_previews", show_world_previews);
config_mapping.get("custom_title_levels", custom_title_levels);

std::optional<ReaderMapping> config_integrations_mapping;
Expand Down Expand Up @@ -374,6 +376,7 @@ Config::save()
writer.write("pause_on_focusloss", pause_on_focusloss);
writer.write("custom_mouse_cursor", custom_mouse_cursor);
writer.write("do_release_check", do_release_check);
writer.write("show_world_previews", show_world_previews);
writer.write("custom_title_levels", custom_title_levels);

writer.start_list("integrations");
Expand Down
1 change: 1 addition & 0 deletions src/supertux/gameconfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class Config final
bool pause_on_focusloss;
bool custom_mouse_cursor;
bool do_release_check;
bool show_world_previews;
bool custom_title_levels;

#ifdef ENABLE_DISCORD
Expand Down
Loading