clang-format

This commit is contained in:
2025-08-10 14:49:49 +02:00
parent d21a474754
commit 8da2db9686
10 changed files with 297 additions and 269 deletions

View File

@@ -1,10 +1,11 @@
#include "asset.h"
#include <SDL3/SDL.h>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <sstream>
#include "utils.h"
@@ -31,7 +32,8 @@ void Asset::addToMap(const std::string &file_path, Type type, bool required, boo
// Verificar si ya existe el archivo
if (file_list_.find(filename) != file_list_.end()) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Asset '%s' already exists, overwriting", filename.c_str());
"Warning: Asset '%s' already exists, overwriting",
filename.c_str());
}
file_list_.emplace(filename, Item{std::move(full_path), type, required});
@@ -48,7 +50,8 @@ void Asset::loadFromFile(const std::string &config_file_path, const std::string
std::ifstream file(config_file_path);
if (!file.is_open()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error: Cannot open config file: %s", config_file_path.c_str());
"Error: Cannot open config file: %s",
config_file_path.c_str());
return;
}
@@ -82,7 +85,8 @@ void Asset::loadFromFile(const std::string &config_file_path, const std::string
// Verificar que tenemos al menos tipo y ruta
if (parts.size() < 2) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Malformed line %d in config file (insufficient fields)", line_number);
"Warning: Malformed line %d in config file (insufficient fields)",
line_number);
continue;
}
@@ -110,12 +114,15 @@ void Asset::loadFromFile(const std::string &config_file_path, const std::string
} catch (const std::exception &e) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Error parsing line %d in config file: %s", line_number, e.what());
"Error parsing line %d in config file: %s",
line_number,
e.what());
}
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Loaded %d assets from config file", static_cast<int>(file_list_.size()));
"Loaded %d assets from config file",
static_cast<int>(file_list_.size()));
file.close();
}
@@ -143,7 +150,7 @@ auto Asset::check() const -> bool {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES");
// Agrupar por tipo para mostrar organizado
std::unordered_map<Type, std::vector<const Item*>> by_type;
std::unordered_map<Type, std::vector<const Item *>> by_type;
for (const auto &[filename, item] : file_list_) {
if (item.required) {
@@ -157,7 +164,8 @@ auto Asset::check() const -> bool {
if (by_type.find(asset_type) != by_type.end()) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"\n>> %s FILES", getTypeName(asset_type).c_str());
"\n>> %s FILES",
getTypeName(asset_type).c_str());
bool type_success = true;
for (const auto *item : by_type[asset_type]) {
@@ -191,7 +199,8 @@ auto Asset::checkFile(const std::string &path) -> bool {
if (!success) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Checking file: %s [ ERROR ]", getFileName(path).c_str());
"Checking file: %s [ ERROR ]",
getFileName(path).c_str());
}
return success;
@@ -233,16 +242,26 @@ auto Asset::parseAssetType(const std::string &type_str) -> Type {
// Devuelve el nombre del tipo de recurso
auto Asset::getTypeName(Type type) -> std::string {
switch (type) {
case Type::BITMAP: return "BITMAP";
case Type::MUSIC: return "MUSIC";
case Type::SOUND: return "SOUND";
case Type::FONT: return "FONT";
case Type::LANG: return "LANG";
case Type::DATA: return "DATA";
case Type::DEMODATA: return "DEMODATA";
case Type::ANIMATION: return "ANIMATION";
case Type::PALETTE: return "PALETTE";
default: return "ERROR";
case Type::BITMAP:
return "BITMAP";
case Type::MUSIC:
return "MUSIC";
case Type::SOUND:
return "SOUND";
case Type::FONT:
return "FONT";
case Type::LANG:
return "LANG";
case Type::DATA:
return "DATA";
case Type::DEMODATA:
return "DEMODATA";
case Type::ANIMATION:
return "ANIMATION";
case Type::PALETTE:
return "PALETTE";
default:
return "ERROR";
}
}

View File

@@ -2,8 +2,8 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <utility>
#include <vector>
// Clase Asset: gestor optimizado de recursos (singleton)
class Asset {
@@ -32,7 +32,7 @@ class Asset {
// --- Métodos para la gestión de recursos ---
void add(const std::string &file_path, Type type, bool required = true, bool absolute = false);
void loadFromFile(const std::string &config_file_path, const std::string &prefix = "", const std::string &system_folder = ""); // Con soporte para variables
[[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original
[[nodiscard]] auto get(const std::string &filename) const -> std::string; // Mantener nombre original
[[nodiscard]] auto check() const -> bool;
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
[[nodiscard]] auto exists(const std::string &filename) const -> bool; // Nueva función para verificar existencia
@@ -40,17 +40,17 @@ class Asset {
private:
// --- Estructura interna para almacenar información de cada recurso ---
struct Item {
std::string file; // Ruta completa del archivo (mantener nombre original)
Type type; // Tipo de recurso
bool required; // Indica si el archivo es obligatorio
std::string file; // Ruta completa del archivo (mantener nombre original)
Type type; // Tipo de recurso
bool required; // Indica si el archivo es obligatorio
Item(std::string path, Type asset_type, bool is_required)
: file(std::move(path)), type(asset_type), required(is_required) {}
Item(std::string path, Type asset_type, bool is_required)
: file(std::move(path)), type(asset_type), required(is_required) {}
};
// --- Variables internas ---
std::unordered_map<std::string, Item> file_list_; // Mapa para búsqueda O(1) (mantener nombre original)
std::string executable_path_; // Ruta del ejecutable
std::string executable_path_; // Ruta del ejecutable
// --- Métodos internos ---
[[nodiscard]] static auto checkFile(const std::string &path) -> bool;

View File

@@ -247,7 +247,7 @@ class GamepadManager {
private:
static constexpr std::string_view UNASSIGNED_TEXT = "---";
static constexpr size_t MAX_PLAYERS = 2;
std::array<Gamepad, MAX_PLAYERS> gamepads_; // Punteros a las estructuras de mandos de Options
std::array<Gamepad, MAX_PLAYERS> gamepads_; // Punteros a las estructuras de mandos de Options
std::vector<std::shared_ptr<Player>> players_; // Punteros a los jugadores
// Convierte Player::Id a índice del array

View File

@@ -460,26 +460,21 @@ void Player::setAnimation() {
player_sprite_->setFlip(flipMode);
break;
}
case State::WAITING:
player_sprite_->setCurrentAnimation("hello");
break;
case State::ROLLING:
case State::CONTINUE_TIME_OUT:
player_sprite_->setCurrentAnimation("rolling");
break;
case State::LYING_ON_THE_FLOOR_FOREVER:
case State::ENTERING_NAME:
case State::CONTINUE:
player_sprite_->setCurrentAnimation("dead");
break;
case State::CELEBRATING:
player_sprite_->setCurrentAnimation("celebration");
break;
default:
break;
}

View File

@@ -13,25 +13,33 @@
// --- Implementación de las estructuras de animación ---
void MenuRenderer::ResizeAnimation::start(float from_w, float from_h, float to_w, float to_h) {
start_width = from_w; start_height = from_h;
target_width = to_w; target_height = to_h;
start_width = from_w;
start_height = from_h;
target_width = to_w;
target_height = to_h;
elapsed = 0.0F;
active = true;
}
void MenuRenderer::ResizeAnimation::stop() { active = false;
void MenuRenderer::ResizeAnimation::stop() {
active = false;
elapsed = 0.0F;
}
void MenuRenderer::ShowHideAnimation::startShow(float to_w, float to_h) {
type = Type::SHOWING; target_width = to_w; target_height = to_h;
type = Type::SHOWING;
target_width = to_w;
target_height = to_h;
elapsed = 0.0F;
active = true;
}
void MenuRenderer::ShowHideAnimation::startHide() { type = Type::HIDING;
void MenuRenderer::ShowHideAnimation::startHide() {
type = Type::HIDING;
elapsed = 0.0F;
active = true;
}
void MenuRenderer::ShowHideAnimation::stop() { type = Type::NONE; active = false;
void MenuRenderer::ShowHideAnimation::stop() {
type = Type::NONE;
active = false;
elapsed = 0.0F;
}
@@ -110,7 +118,7 @@ void MenuRenderer::update(const ServiceMenu *menu_state) {
// --- Nuevos métodos de control ---
void MenuRenderer::show(const ServiceMenu* menu_state) {
void MenuRenderer::show(const ServiceMenu *menu_state) {
if (visible_) {
return;
}
@@ -201,7 +209,6 @@ auto MenuRenderer::calculateNewRect(const ServiceMenu *menu_state) -> SDL_FRect
return new_rect;
}
void MenuRenderer::resize(const ServiceMenu *menu_state) {
SDL_FRect new_rect = calculateNewRect(menu_state);
@@ -286,7 +293,6 @@ void MenuRenderer::updateResizeAnimation(float delta_time) {
options_y_ = rect_.y + upper_height_ + lower_padding_;
}
void MenuRenderer::updatePosition() {
switch (position_mode_) {
case PositionMode::CENTERED:

View File

@@ -77,27 +77,29 @@ class MenuRenderer {
// --- Estructuras de Animación (inspirado en WindowMessage) ---
struct ResizeAnimation {
bool active = false;
float start_width, start_height;
float target_width, target_height;
float elapsed = 0.0F;
float duration = 0.2F;
bool active = false;
float start_width, start_height;
float target_width, target_height;
float elapsed = 0.0F;
float duration = 0.2F;
void start(float from_w, float from_h, float to_w, float to_h);
void stop();
void start(float from_w, float from_h, float to_w, float to_h);
void stop();
} resize_animation_;
struct ShowHideAnimation {
enum class Type { NONE, SHOWING, HIDING };
Type type = Type::NONE;
bool active = false;
float target_width, target_height;
float elapsed = 0.0F;
float duration = 0.25F;
enum class Type { NONE,
SHOWING,
HIDING };
Type type = Type::NONE;
bool active = false;
float target_width, target_height;
float elapsed = 0.0F;
float duration = 0.25F;
void startShow(float to_w, float to_h);
void startHide();
void stop();
void startShow(float to_w, float to_h);
void startHide();
void stop();
} show_hide_animation_;
// --- Anchos precalculados ---

View File

@@ -26,13 +26,11 @@ void WindowMessage::render() {
SDL_Renderer* renderer = Screen::get()->getRenderer();
// Dibujar fondo con transparencia
SDL_SetRenderDrawColor(renderer, config_.bg_color.r, config_.bg_color.g,
config_.bg_color.b, config_.bg_color.a);
SDL_SetRenderDrawColor(renderer, config_.bg_color.r, config_.bg_color.g, config_.bg_color.b, config_.bg_color.a);
SDL_RenderFillRect(renderer, &rect_);
// Dibujar borde
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g,
config_.border_color.b, config_.border_color.a);
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, config_.border_color.b, config_.border_color.a);
SDL_RenderRect(renderer, &rect_);
// Solo mostrar contenido si no estamos en animación de show/hide
@@ -54,8 +52,7 @@ void WindowMessage::render() {
// Línea separadora debajo del título (solo si hay título visible)
if (!visible_title.empty()) {
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g,
config_.border_color.b, config_.border_color.a);
SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, config_.border_color.b, config_.border_color.a);
SDL_RenderLine(renderer,
rect_.x + config_.padding,
current_y - config_.title_separator_spacing / 2.0F,
@@ -91,7 +88,7 @@ void WindowMessage::update() {
void WindowMessage::show() {
if (visible_) {
return; // Ya visible
return; // Ya visible
}
visible_ = true;
@@ -104,12 +101,12 @@ void WindowMessage::show() {
show_hide_animation_.startShow(rect_.w, rect_.h);
rect_.w = 0.0F;
rect_.h = 0.0F;
updatePosition(); // Reposicionar con tamaño 0
updatePosition(); // Reposicionar con tamaño 0
}
void WindowMessage::hide() {
if (!visible_) {
return; // Ya oculto
return; // Ya oculto
}
// Detener cualquier animación anterior
@@ -158,7 +155,7 @@ void WindowMessage::setPosition(float x, float y, PositionMode mode) {
void WindowMessage::setSize(float width, float height) {
rect_.w = width;
rect_.h = height;
updatePosition(); // Reposicionar después de cambiar el tamaño
updatePosition(); // Reposicionar después de cambiar el tamaño
}
void WindowMessage::centerOnScreen() {
@@ -167,11 +164,11 @@ void WindowMessage::centerOnScreen() {
void WindowMessage::autoSize() {
if (show_hide_animation_.active) {
return; // No redimensionar durante show/hide
return; // No redimensionar durante show/hide
}
if (resize_animation_.active) {
resize_animation_.stop(); // Detener animación anterior
resize_animation_.stop(); // Detener animación anterior
}
float old_width = rect_.w;
@@ -186,21 +183,19 @@ void WindowMessage::autoSize() {
rect_.w = old_width;
rect_.h = old_height;
} else {
updatePosition(); // Reposicionar después de ajustar el tamaño
updatePosition(); // Reposicionar después de ajustar el tamaño
}
}
void WindowMessage::updateStyles() {
title_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.title_color,
config_.title_color, 0, -2);
text_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.text_color,
config_.text_color, 0, -2);
title_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.title_color, config_.title_color, 0, -2);
text_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.text_color, config_.text_color, 0, -2);
}
void WindowMessage::updatePosition() {
switch (position_mode_) {
case PositionMode::CENTERED:
rect_.x = anchor_.x- rect_.w / 2.0F;
rect_.x = anchor_.x - rect_.w / 2.0F;
rect_.y = anchor_.y - rect_.h / 2.0F;
break;
case PositionMode::FIXED:
@@ -220,7 +215,7 @@ void WindowMessage::ensureTextFits() {
// Verificar si el tamaño actual es suficiente
if (rect_.w < required_width || rect_.h < required_height) {
autoSize(); // Recalcular tamaño automáticamente
autoSize(); // Recalcular tamaño automáticamente
}
}
@@ -264,7 +259,7 @@ auto WindowMessage::calculateContentHeight() const -> float {
}
auto WindowMessage::calculateContentWidth() const -> float {
float max_width = config_.min_width - (config_.padding * 2); // Ancho mínimo sin padding
float max_width = config_.min_width - (config_.padding * 2); // Ancho mínimo sin padding
// Ancho del título
if (!title_.empty()) {
@@ -341,7 +336,7 @@ void WindowMessage::updateShowHideAnimation(float delta_time) {
rect_.h = show_hide_animation_.target_height * (1.0F - progress);
}
updatePosition(); // Mantener la posición centrada durante la animación
updatePosition(); // Mantener la posición centrada durante la animación
}
}
@@ -363,11 +358,11 @@ void WindowMessage::updateResizeAnimation(float delta_time) {
float progress = easeOut(resize_animation_.getProgress(config_.animation_duration));
rect_.w = resize_animation_.start_width +
(resize_animation_.target_width - resize_animation_.start_width) * progress;
(resize_animation_.target_width - resize_animation_.start_width) * progress;
rect_.h = resize_animation_.start_height +
(resize_animation_.target_height - resize_animation_.start_height) * progress;
(resize_animation_.target_height - resize_animation_.start_height) * progress;
updatePosition(); // Mantener la posición centrada durante la animación
updatePosition(); // Mantener la posición centrada durante la animación
}
}

View File

@@ -17,34 +17,34 @@ class WindowMessage {
};
struct Config {
// Colores
Color bg_color;
Color border_color;
Color title_color;
Color text_color;
// Colores
Color bg_color;
Color border_color;
Color title_color;
Color text_color;
// Espaciado y dimensiones
float padding{15.0f};
float line_spacing{5.0f};
float title_separator_spacing{10.0f}; // Espacio extra para separador del título
// Espaciado y dimensiones
float padding{15.0f};
float line_spacing{5.0f};
float title_separator_spacing{10.0f}; // Espacio extra para separador del título
// Límites de tamaño
float min_width{200.0f};
float min_height{100.0f};
float max_width_ratio{0.8f}; // % máximo de ancho de pantalla
float max_height_ratio{0.8f}; // % máximo de alto de pantalla
// Límites de tamaño
float min_width{200.0f};
float min_height{100.0f};
float max_width_ratio{0.8f}; // % máximo de ancho de pantalla
float max_height_ratio{0.8f}; // % máximo de alto de pantalla
// Margen de seguridad para texto
float text_safety_margin{20.0f}; // Margen extra para evitar texto cortado
// Margen de seguridad para texto
float text_safety_margin{20.0f}; // Margen extra para evitar texto cortado
// Animaciones
float animation_duration{0.3f}; // Duración en segundos para todas las animaciones
// Animaciones
float animation_duration{0.3f}; // Duración en segundos para todas las animaciones
// Constructor con valores por defecto
Config()
: bg_color{40, 40, 60, 220}, border_color{100, 100, 120, 255}, title_color{255, 255, 255, 255}, text_color{200, 200, 200, 255}
// Constructor con valores por defecto
Config()
: bg_color{40, 40, 60, 220}, border_color{100, 100, 120, 255}, title_color{255, 255, 255, 255}, text_color{200, 200, 200, 255}
{}
{}
};
WindowMessage(
@@ -78,20 +78,29 @@ class WindowMessage {
void setPosition(float x, float y, PositionMode mode = PositionMode::CENTERED);
void setSize(float width, float height);
void centerOnScreen();
void autoSize(); // Ajusta automáticamente al contenido y reposiciona si es necesario
void autoSize(); // Ajusta automáticamente al contenido y reposiciona si es necesario
// Configuración de colores
void setBackgroundColor(const Color& color) { config_.bg_color = color; }
void setBorderColor(const Color& color) { config_.border_color = color; }
void setTitleColor(const Color& color) { config_.title_color = color; updateStyles(); }
void setTextColor(const Color& color) { config_.text_color = color; updateStyles(); }
void setTitleColor(const Color& color) {
config_.title_color = color;
updateStyles();
}
void setTextColor(const Color& color) {
config_.text_color = color;
updateStyles();
}
// Configuración de espaciado
void setPadding(float padding) { config_.padding = padding; }
void setLineSpacing(float spacing) { config_.line_spacing = spacing; }
// Configuración avanzada
void setConfig(const Config& config) { config_ = config; updateStyles(); }
void setConfig(const Config& config) {
config_ = config;
updateStyles();
}
[[nodiscard]] auto getConfig() const -> const Config& { return config_; }
// Getters
@@ -105,7 +114,7 @@ class WindowMessage {
// Estado de visibilidad y redimensionado
bool visible_ = false;
bool auto_resize_enabled_ = true; // Por defecto habilitado
bool auto_resize_enabled_ = true; // Por defecto habilitado
// Contenido
std::string title_;
@@ -118,70 +127,72 @@ class WindowMessage {
// Animación de redimensionado
struct ResizeAnimation {
bool active = false;
float start_width, start_height;
float target_width, target_height;
float elapsed = 0.0F;
bool active = false;
float start_width, start_height;
float target_width, target_height;
float elapsed = 0.0F;
void start(float from_w, float from_h, float to_w, float to_h) {
start_width = from_w;
start_height = from_h;
target_width = to_w;
target_height = to_h;
elapsed = 0.0F;
active = true;
}
void start(float from_w, float from_h, float to_w, float to_h) {
start_width = from_w;
start_height = from_h;
target_width = to_w;
target_height = to_h;
elapsed = 0.0F;
active = true;
}
void stop() {
active = false;
elapsed = 0.0F;
}
void stop() {
active = false;
elapsed = 0.0F;
}
[[nodiscard]] auto isFinished(float duration) const -> bool {
return elapsed >= duration;
}
[[nodiscard]] auto isFinished(float duration) const -> bool {
return elapsed >= duration;
}
[[nodiscard]] auto getProgress(float duration) const -> float {
return std::min(elapsed / duration, 1.0F);
}
[[nodiscard]] auto getProgress(float duration) const -> float {
return std::min(elapsed / duration, 1.0F);
}
} resize_animation_;
// Animación de mostrar/ocultar
struct ShowHideAnimation {
enum class Type { NONE, SHOWING, HIDING };
enum class Type { NONE,
SHOWING,
HIDING };
Type type = Type::NONE;
bool active = false;
float target_width, target_height; // Tamaño final al mostrar
float elapsed = 0.0F;
Type type = Type::NONE;
bool active = false;
float target_width, target_height; // Tamaño final al mostrar
float elapsed = 0.0F;
void startShow(float to_w, float to_h) {
type = Type::SHOWING;
target_width = to_w;
target_height = to_h;
elapsed = 0.0F;
active = true;
}
void startShow(float to_w, float to_h) {
type = Type::SHOWING;
target_width = to_w;
target_height = to_h;
elapsed = 0.0F;
active = true;
}
void startHide() {
type = Type::HIDING;
elapsed = 0.0F;
active = true;
}
void startHide() {
type = Type::HIDING;
elapsed = 0.0F;
active = true;
}
void stop() {
type = Type::NONE;
active = false;
elapsed = 0.0F;
}
void stop() {
type = Type::NONE;
active = false;
elapsed = 0.0F;
}
[[nodiscard]] auto isFinished(float duration) const -> bool {
return elapsed >= duration;
}
[[nodiscard]] auto isFinished(float duration) const -> bool {
return elapsed >= duration;
}
[[nodiscard]] auto getProgress(float duration) const -> float {
return std::min(elapsed / duration, 1.0F);
}
[[nodiscard]] auto getProgress(float duration) const -> float {
return std::min(elapsed / duration, 1.0F);
}
} show_hide_animation_;
// Estilos
@@ -190,13 +201,13 @@ class WindowMessage {
// Métodos privados
void calculateAutoSize();
void updatePosition(); // Actualiza la posición según el modo y punto de anclaje
void updateStyles(); // Actualiza los estilos de texto cuando cambian los colores
void ensureTextFits(); // Verifica y ajusta para que todo el texto sea visible
void triggerAutoResize(); // Inicia redimensionado automático si está habilitado
void updateAnimation(float delta_time); // Actualiza la animación de redimensionado
void updateShowHideAnimation(float delta_time); // Actualiza la animación de mostrar/ocultar
void updateResizeAnimation(float delta_time); // Actualiza la animación de redimensionado
void updatePosition(); // Actualiza la posición según el modo y punto de anclaje
void updateStyles(); // Actualiza los estilos de texto cuando cambian los colores
void ensureTextFits(); // Verifica y ajusta para que todo el texto sea visible
void triggerAutoResize(); // Inicia redimensionado automático si está habilitado
void updateAnimation(float delta_time); // Actualiza la animación de redimensionado
void updateShowHideAnimation(float delta_time); // Actualiza la animación de mostrar/ocultar
void updateResizeAnimation(float delta_time); // Actualiza la animación de redimensionado
// Función de suavizado (ease-out)
[[nodiscard]] static auto easeOut(float t) -> float;
@@ -204,7 +215,7 @@ class WindowMessage {
// Métodos para manejo de texto durante animación
[[nodiscard]] auto getTruncatedText(const std::string& text, float available_width) const -> std::string;
[[nodiscard]] auto getAvailableTextWidth() const -> float;
[[nodiscard]] auto shouldShowContent() const -> bool; // Si mostrar el contenido (texto, líneas, etc.)
[[nodiscard]] auto shouldShowContent() const -> bool; // Si mostrar el contenido (texto, líneas, etc.)
[[nodiscard]] auto calculateContentHeight() const -> float;
[[nodiscard]] auto calculateContentWidth() const -> float;