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

Add slider menu item, configuring on-screen controls size #3021

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
13 changes: 5 additions & 8 deletions src/control/mobile_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,17 @@ MobileController::draw(DrawingContext& context)

if (m_screen_width != static_cast<int>(context.get_width()) ||
m_screen_height != static_cast<int>(context.get_height()) ||
m_mobile_controls_scale != g_config->m_mobile_controls_scale)
m_mobile_controls_scale != g_config->mobile_controls_scale)
{
m_screen_width = static_cast<int>(context.get_width());
m_screen_height = static_cast<int>(context.get_height());
float width = static_cast<float>(m_screen_width);
float height = static_cast<float>(m_screen_height);
m_mobile_controls_scale = g_config->m_mobile_controls_scale;
// Buttons on Android are bigger, and direction buttons are extra wide
m_mobile_controls_scale = g_config->mobile_controls_scale;

// Use screen height to calculate button size, because 20:9 screen ratios are common
#ifdef __ANDROID__
const float BUTTON_SCALE = 0.4f * g_config->m_mobile_controls_scale;
#else
const float BUTTON_SCALE = 0.2f * g_config->m_mobile_controls_scale;
#endif
const float BUTTON_SCALE = 0.05f * static_cast<float>(g_config->mobile_controls_scale);

m_rect_directions.set_size(height * BUTTON_SCALE * 4 / 3, height * BUTTON_SCALE);
m_rect_directions.set_pos(Vector(0, height - height * BUTTON_SCALE));
m_draw_directions = Rectf::from_center(m_rect_directions.get_middle(),
Expand Down
2 changes: 1 addition & 1 deletion src/control/mobile_controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class MobileController final
m_tex_jump, m_tex_action, m_tex_cheats, m_tex_debug;

int m_screen_width, m_screen_height;
float m_mobile_controls_scale;
int m_mobile_controls_scale;
Vankata453 marked this conversation as resolved.
Show resolved Hide resolved

private:
MobileController(const MobileController&) = delete;
Expand Down
129 changes: 129 additions & 0 deletions src/gui/item_slider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// SuperTux
// Copyright (C) 2024 Vankata453
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "gui/item_slider.hpp"

#include "gui/menu_manager.hpp"
#include "supertux/gameconfig.hpp"
#include "supertux/globals.hpp"
#include "supertux/resources.hpp"
#include "video/color.hpp"
#include "video/drawing_context.hpp"
#include "video/video_system.hpp"
#include "video/viewport.hpp"

static const float SLIDER_WIDTH = 100.f;

ItemSlider::ItemSlider(const std::string& text, int min_value, int max_value, int* value, const std::string& value_append, int id) :
MenuItem(text, id),
m_min_value(min_value),
m_max_value(max_value),
m_value(value),
m_value_append(value_append),
m_slider_x(-SLIDER_WIDTH), // Will be set in draw().
m_sliding(false)
{
assert(m_min_value < m_max_value);
}

void
ItemSlider::draw(DrawingContext& context, const Vector& pos, int menu_width, bool active)
{
assert(*m_value >= m_min_value && *m_value <= m_max_value);

const float value_text_width = Resources::normal_font->get_text_width(std::to_string(m_max_value) + m_value_append);

context.color().draw_text(Resources::normal_font, get_text(),
Vector(pos.x + 16.f,
pos.y - Resources::normal_font->get_height() / 2.f),
ALIGN_LEFT, LAYER_GUI, active ? g_config->activetextcolor : get_color());

m_slider_x = pos.x + static_cast<float>(menu_width) - SLIDER_WIDTH - value_text_width - 32.f;
context.color().draw_filled_rect(Rectf(Vector(m_slider_x, pos.y - 1.f),
Vector(pos.x + static_cast<float>(menu_width) - value_text_width - 32.f, pos.y + 1.f)),
active ? Color::BLACK : get_color(), LAYER_GUI);

const float slider_indicator_x = m_slider_x + (static_cast<float>(*m_value - m_min_value) / static_cast<float>(m_max_value - m_min_value)) * SLIDER_WIDTH;
context.color().draw_filled_rect(Rectf(Vector(slider_indicator_x - 2.f, pos.y - Resources::normal_font->get_height() / 2 + 1.f),
Vector(slider_indicator_x + 2.f, pos.y + Resources::normal_font->get_height() / 2 - 1.f)),
active ? Color::BLACK : get_color(), LAYER_GUI);

context.color().draw_text(Resources::normal_font, std::to_string(*m_value) + m_value_append,
Vector(pos.x + static_cast<float>(menu_width) - 16.f,
pos.y - Resources::normal_font->get_height() / 2.f),
ALIGN_RIGHT, LAYER_GUI, active ? g_config->activetextcolor : get_color());
}

int
ItemSlider::get_width() const
{
const float value_text_width = Resources::normal_font->get_text_width(std::to_string(m_max_value) + m_value_append);
return static_cast<int>(Resources::normal_font->get_text_width(get_text()) + SLIDER_WIDTH + value_text_width + 48.f);
}

void
ItemSlider::event(const SDL_Event& ev)
{
switch (ev.type)
{
case SDL_MOUSEBUTTONDOWN:
{
if (ev.button.button == SDL_BUTTON_LEFT)
{
const Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(ev.motion.x, ev.motion.y);
if (mouse_pos.x >= m_slider_x && mouse_pos.x <= m_slider_x + SLIDER_WIDTH)
{
*m_value = ((static_cast<float>(mouse_pos.x) - m_slider_x) / SLIDER_WIDTH) * (m_max_value - m_min_value) + m_min_value;
m_sliding = true;

MenuManager::instance().current_menu()->menu_action(*this);
}
}
break;
}

case SDL_MOUSEBUTTONUP:
if (ev.button.button == SDL_BUTTON_LEFT)
m_sliding = false;
break;

case SDL_MOUSEMOTION:
if (m_sliding)
{
const Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(ev.motion.x, ev.motion.y);
if (mouse_pos.x <= m_slider_x)
*m_value = m_min_value;
else if (mouse_pos.x >= m_slider_x + SLIDER_WIDTH)
*m_value = m_max_value;
else
*m_value = ((static_cast<float>(mouse_pos.x) - m_slider_x) / SLIDER_WIDTH) * (m_max_value - m_min_value) + m_min_value;

MenuManager::instance().current_menu()->menu_action(*this);
}
break;

case SDL_KEYDOWN:
if (ev.key.keysym.sym == SDLK_LEFT)
*m_value = std::max(*m_value - 1, m_min_value);
else if (ev.key.keysym.sym == SDLK_RIGHT)
*m_value = std::min(*m_value + 1, m_max_value);

MenuManager::instance().current_menu()->menu_action(*this);
break;
}
}

/* EOF */
55 changes: 55 additions & 0 deletions src/gui/item_slider.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SuperTux
// Copyright (C) 2024 Vankata453
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#ifndef HEADER_SUPERTUX_GUI_ITEM_SLIDER_HPP
#define HEADER_SUPERTUX_GUI_ITEM_SLIDER_HPP

#include "gui/menu_item.hpp"

class ItemSlider final : public MenuItem
{
public:
ItemSlider(const std::string& text, int min_value, int max_value, int* value, const std::string& value_append = {}, int id = -1);

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

/** Processes the menu action. */
void event(const SDL_Event& ev) override;

/** Returns the minimum width of the menu item. */
int get_width() const override;

bool changes_width() const override { return false; }
bool locks_selection() const { return m_sliding; }

private:
int m_min_value;
int m_max_value;
int* m_value;
const std::string m_value_append;

float m_slider_x;
bool m_sliding;

private:
ItemSlider(const ItemSlider&) = delete;
ItemSlider& operator=(const ItemSlider&) = delete;
};

#endif

/* EOF */
19 changes: 19 additions & 0 deletions src/gui/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "gui/item_paths.hpp"
#include "gui/item_script.hpp"
#include "gui/item_script_line.hpp"
#include "gui/item_slider.hpp"
#include "gui/item_stringselect.hpp"
#include "gui/item_textfield.hpp"
#include "gui/item_list.hpp"
Expand Down Expand Up @@ -400,6 +401,15 @@ Menu::add_horizontalmenu(int id, float height, float min_item_width)
return *item_ptr;
}

ItemSlider&
Menu::add_slider(const std::string& text, int min_value, int max_value, int* value, const std::string& value_append, int id)
{
auto item = std::make_unique<ItemSlider>(text, min_value, max_value, value, value_append, id);
auto item_ptr = item.get();
add_item(std::move(item));
return *item_ptr;
}

void
Menu::clear()
{
Expand Down Expand Up @@ -449,6 +459,9 @@ Menu::process_action(const MenuAction& action)

switch (action) {
case MenuAction::UP:
if (m_items[m_active_item]->locks_selection())
return;

do {
if (m_active_item > 0)
--m_active_item;
Expand All @@ -459,6 +472,9 @@ Menu::process_action(const MenuAction& action)
break;

case MenuAction::DOWN:
if (m_items[m_active_item]->locks_selection())
return;

do {
if (m_active_item < int(m_items.size())-1 )
++m_active_item;
Expand Down Expand Up @@ -688,6 +704,9 @@ Menu::event(const SDL_Event& ev)

case SDL_MOUSEMOTION:
{
if (m_items[m_active_item]->locks_selection())
break;

Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(ev.motion.x, ev.motion.y);
float x = mouse_pos.x;
float y = mouse_pos.y;
Expand Down
2 changes: 2 additions & 0 deletions src/gui/menu.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ItemLabel;
class ItemPaths;
class ItemScript;
class ItemScriptLine;
class ItemSlider;
class ItemList;
class ItemStringSelect;
class ItemTextField;
Expand Down Expand Up @@ -109,6 +110,7 @@ class Menu
ItemImages& add_images(const std::vector<std::string>& image_paths, int max_image_width = 0, int max_image_height = 0, int id = -1);
ItemList& add_list(const std::string& text, const std::vector<std::string>& items, std::string* value_ptr, int id = -1);
ItemHorizontalMenu& add_horizontalmenu(int id, float height, float min_item_width = -1.f);
ItemSlider& add_slider(const std::string& text, int min_value, int max_value, int* value, const std::string& value_append = {}, int id = -1);

/** Remove all entries from the menu */
void clear();
Expand Down
3 changes: 3 additions & 0 deletions src/gui/menu_item.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class MenuItem
return false;
}

/** Returns true when the menu shouldn't move the selection from this item. */
virtual bool locks_selection() const { return false; }

/** Returns true when the width must be recalculated when an action is
processed */
virtual bool changes_width() const {
Expand Down
11 changes: 8 additions & 3 deletions src/supertux/gameconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ Config::Config() :
keyboard_config(),
joystick_config(),
mobile_controls(SDL_GetNumTouchDevices() > 0),
m_mobile_controls_scale(1),
#ifdef __ANDROID__
mobile_controls_scale(8), // Buttons on Android are bigger, and direction buttons are extra wide
#else
mobile_controls_scale(4),
#endif
addons(),
developer_mode(false),
christmas_mode(false),
Expand Down Expand Up @@ -323,7 +327,7 @@ Config::load()
}

config_control_mapping->get("mobile_controls", mobile_controls, SDL_GetNumTouchDevices() > 0);
config_control_mapping->get("mobile_controls_scale", m_mobile_controls_scale, 1);
config_control_mapping->get("mobile_controls_scale", mobile_controls_scale);
}

std::optional<ReaderCollection> config_addons_mapping;
Expand Down Expand Up @@ -473,7 +477,7 @@ Config::save()
writer.end_list("joystick");

writer.write("mobile_controls", mobile_controls);
writer.write("mobile_controls_scale", m_mobile_controls_scale);
writer.write("mobile_controls_scale", mobile_controls_scale);
}
writer.end_list("control");

Expand Down Expand Up @@ -510,6 +514,7 @@ void
Config::check_values()
{
camera_peek_multiplier = math::clamp(camera_peek_multiplier, 0.f, 1.f);
mobile_controls_scale = math::clamp(mobile_controls_scale, 4, 12);
}

bool
Expand Down
2 changes: 1 addition & 1 deletion src/supertux/gameconfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class Config final
JoystickConfig joystick_config;

bool mobile_controls;
float m_mobile_controls_scale;
int mobile_controls_scale;

struct Addon
{
Expand Down
Loading
Loading