Skip to content

Commit

Permalink
Add slider menu item, configuring on-screen controls size
Browse files Browse the repository at this point in the history
Adds the `ItemSlider` menu item. It allows selecting an integer value between a minimum and a maximum value. It can be used with the left mouse button and the left/right arrow keys.

The sound volume, music volume and flash intensity options now utilize a slider. The on-screen controls scale option was added back in "Options" -> "Controls" and utilizes a slider as well.

Additionally:

* The on-screen controls toggle has also been re-added in "Options" -> "Controls".
* The on-screen controls configuration values were changed to allow for broader scale customization.

Closes #3010.
  • Loading branch information
Vankata453 committed Jul 15, 2024
1 parent 60f5cb3 commit 09678db
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 168 deletions.
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 * 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
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)
{
}

void
ItemSlider::draw(DrawingContext& context, const Vector& pos, int menu_width, bool active)
{
assert(m_min_value < m_max_value);
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

0 comments on commit 09678db

Please sign in to comment.