Save Linear VIew colors, sizes and lane order

This commit is contained in:
Stepland 2022-11-19 03:29:49 +01:00
parent 04654e268f
commit ddb0b1112a
9 changed files with 284 additions and 148 deletions

View File

@ -15,34 +15,37 @@ struct RectangleColors {
sf::Color border;
};
struct LinearViewColors {
sf::Color cursor = {66, 150, 250, 170};
RectangleColors tab_selection = {
.fill = {153, 255, 153, 92},
.border = {153, 255, 153, 189}
};
sf::Color normal_tap_note = {255, 213, 0};
sf::Color conflicting_tap_note = {255, 167, 0};
sf::Color normal_collision_zone = {230, 179, 0, 80};
sf::Color conflicting_collision_zone = {255, 0, 0, 145};
sf::Color normal_long_note = {255, 90, 0, 223};
sf::Color conflicting_long_note = {255, 26, 0};
sf::Color selected_note_fill = {255, 255, 255, 127};
sf::Color selected_note_outline = sf::Color::White;
sf::Color measure_line = sf::Color::White;
sf::Color measure_number = sf::Color::White;
sf::Color beat_line = {255, 255, 255, 127};
ButtonColors bpm_button = {
.text = {66, 150, 250},
.button = sf::Color::Transparent,
.hover = {66, 150, 250, 64},
.active = {66, 150, 250, 127},
.border = {109, 179, 251}
};
RectangleColors selection_rect = {
.fill = {144, 189, 255, 64},
.border = {144, 189, 255}
namespace linear_view {
struct Colors {
sf::Color cursor = {66, 150, 250, 170};
RectangleColors tab_selection = {
.fill = {153, 255, 153, 92},
.border = {153, 255, 153, 189}
};
sf::Color normal_tap_note = {255, 213, 0};
sf::Color conflicting_tap_note = {255, 167, 0};
sf::Color normal_collision_zone = {230, 179, 0, 80};
sf::Color conflicting_collision_zone = {255, 0, 0, 145};
sf::Color normal_long_note = {255, 90, 0, 223};
sf::Color conflicting_long_note = {255, 26, 0};
sf::Color selected_note_fill = {255, 255, 255, 127};
sf::Color selected_note_outline = sf::Color::White;
sf::Color measure_line = sf::Color::White;
sf::Color measure_number = sf::Color::White;
sf::Color beat_line = {255, 255, 255, 127};
ButtonColors bpm_button = {
.text = {66, 150, 250},
.button = sf::Color::Transparent,
.hover = {66, 150, 250, 64},
.active = {66, 150, 250, 127},
.border = {109, 179, 251}
};
RectangleColors selection_rect = {
.fill = {144, 189, 255, 64},
.border = {144, 189, 255}
};
};
const linear_view::Colors default_colors = {};
};
inline const LinearViewColors default_linear_view_colors = {};

View File

@ -4,9 +4,12 @@
#include <filesystem>
#include <fmt/format.h>
#include <toml++/toml.h>
#include <variant>
#include "colors.hpp"
#include "marker.hpp"
#include "variant_visitor.hpp"
#include "widgets/lane_order.hpp"
toml::array config::dump_color(const sf::Color& color) {
return toml::array{color.r, color.g, color.b, color.a};
@ -85,8 +88,8 @@ void config::Marker::dump_as_v1_0_0(toml::table &tbl) {
tbl.insert_or_assign("marker", marker_table);
}
LinearViewColors config::load_linear_view_colors_from_v1_0_0_table(const toml::table& linear_view) {
auto colors = default_linear_view_colors;
linear_view::Colors config::load_linear_view_colors_from_v1_0_0_table(const toml::table& linear_view) {
auto colors = linear_view::default_colors;
const auto colors_node = linear_view["colors"];
load_color(colors_node["cursor"], colors.cursor);
load_color(colors_node["tab_selection"]["fill"], colors.tab_selection.fill);
@ -112,7 +115,7 @@ LinearViewColors config::load_linear_view_colors_from_v1_0_0_table(const toml::t
return colors;
}
void config::dump_linear_view_colors_as_v1_0_0(const LinearViewColors& colors, toml::table& linear_view) {
void config::dump_linear_view_colors_as_v1_0_0(const linear_view::Colors& colors, toml::table& linear_view) {
toml::table colors_table{
{"cursor", dump_color(colors.cursor)},
{"tab_selection", toml::table{
@ -145,15 +148,89 @@ void config::dump_linear_view_colors_as_v1_0_0(const LinearViewColors& colors, t
linear_view.insert_or_assign("colors", colors_table);
}
void config::LinearView::load_from_v1_0_0_table(const toml::table& tbl) {
if (tbl["linear_view"].is_table()) {
colors = load_linear_view_colors_from_v1_0_0_table(tbl["linear_view"].ref<toml::table>());
linear_view::Sizes config::load_linear_view_sizes_from_v1_0_0_table(const toml::table& linear_view) {
auto sizes = linear_view::default_sizes;
const auto sizes_node = linear_view["sizes"];
sizes.timeline_margin = sizes_node["timeline_margin"].value<int>().value_or(sizes.timeline_margin);
sizes.cursor_height = sizes_node["cursor_height"].value<int>().value_or(sizes.cursor_height);
return sizes;
}
void config::dump_linear_view_sizes_as_v1_0_0(const linear_view::Sizes& sizes, toml::table& linear_view) {
toml::table sizes_table{
{"timeline_margin", sizes.timeline_margin},
{"cursor_height", sizes.cursor_height},
};
linear_view.insert_or_assign("sizes", sizes_table);
}
linear_view::LaneOrder config::load_linear_view_lane_order_from_v1_0_0_table(const toml::table& linear_view) {
auto lane_order = linear_view::default_lane_order;
const auto lane_order_node = linear_view["lane_order"];
const auto type = lane_order_node["type"].value<std::string>();
if (not type) {
return lane_order;
}
if (*type == "default") {
return linear_view::lane_order::Default{};
} else if (*type == "vertical") {
return linear_view::lane_order::Vertical{};
} else if (*type == "custom") {
const auto order_as_string = lane_order_node["order"].value<std::string>();
if (order_as_string) {
return linear_view::lane_order::Custom{*order_as_string};
}
}
return lane_order;
}
void config::dump_linear_view_lane_order_as_v1_0_0(const linear_view::LaneOrder& lane_order, toml::table& linear_view) {
const auto _dump = VariantVisitor {
[&](const linear_view::lane_order::Default&) {
linear_view.insert_or_assign(
"lane_order",
toml::table{{"type", "default"}}
);
},
[&](const linear_view::lane_order::Vertical&) {
linear_view.insert_or_assign(
"lane_order",
toml::table{{"type", "vertical"}}
);
},
[&](const linear_view::lane_order::Custom& custom) {
linear_view.insert_or_assign(
"lane_order",
toml::table{
{"type", "custom"},
{"order", custom.as_string}
}
);
}
};
std::visit(_dump, lane_order);
}
void config::LinearView::load_from_v1_0_0_table(const toml::table& tbl) {
if (not tbl["linear_view"].is_table()) {
return;
}
const auto linear_view_table = tbl["linear_view"].ref<toml::table>();
colors = load_linear_view_colors_from_v1_0_0_table(linear_view_table);
sizes = load_linear_view_sizes_from_v1_0_0_table(linear_view_table);
lane_order = load_linear_view_lane_order_from_v1_0_0_table(linear_view_table);
}
void config::LinearView::dump_as_v1_0_0(toml::table& tbl) {
toml::table linear_view;
dump_linear_view_colors_as_v1_0_0(colors, linear_view);
dump_linear_view_sizes_as_v1_0_0(sizes, linear_view);
dump_linear_view_lane_order_as_v1_0_0(lane_order, linear_view);
tbl.insert_or_assign("linear_view", linear_view);
}

View File

@ -5,7 +5,9 @@
#include <toml++/toml.h>
#include "colors.hpp"
#include "sizes.hpp"
#include "marker.hpp"
#include "widgets/lane_order.hpp"
namespace config {
@ -23,11 +25,19 @@ namespace config {
void dump_as_v1_0_0(toml::table& tbl);
};
LinearViewColors load_linear_view_colors_from_v1_0_0_table(const toml::table& linear_view);
void dump_linear_view_colors_as_v1_0_0(const LinearViewColors& colors, toml::table& linear_view);
linear_view::Colors load_linear_view_colors_from_v1_0_0_table(const toml::table& linear_view);
void dump_linear_view_colors_as_v1_0_0(const linear_view::Colors& colors, toml::table& linear_view);
linear_view::Sizes load_linear_view_sizes_from_v1_0_0_table(const toml::table& linear_view);
void dump_linear_view_sizes_as_v1_0_0(const linear_view::Sizes& sizes, toml::table& linear_view);
linear_view::LaneOrder load_linear_view_lane_order_from_v1_0_0_table(const toml::table& linear_view);
void dump_linear_view_lane_order_as_v1_0_0(const linear_view::LaneOrder& lane_order, toml::table& linear_view);
struct LinearView {
LinearViewColors colors;
linear_view::Colors colors;
linear_view::Sizes sizes;
linear_view::LaneOrder lane_order;
void load_from_v1_0_0_table(const toml::table& tbl);
void dump_as_v1_0_0(toml::table& tbl);

10
src/sizes.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
namespace linear_view {
struct Sizes {
int timeline_margin = 130;
int cursor_height = 100;
};
const Sizes default_sizes = {};
}

View File

@ -0,0 +1,61 @@
#include "lane_order.hpp"
#include <sstream>
linear_view::lane_order::Custom::Custom() :
lane_to_button({0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15})
{
update_from_array();
}
linear_view::lane_order::Custom::Custom(const std::string& as_string_) :
as_string(as_string_)
{
cleanup_string();
update_from_string();
}
void linear_view::lane_order::Custom::update_from_array() {
std::stringstream ss;
button_to_lane.clear();
for (std::size_t lane = 0; lane < lane_to_button.size(); lane++) {
const auto button = lane_to_button.at(lane);
if (button) {
ss << letters.at(*button);
button_to_lane[*button] = lane;
} else {
ss << "_";
}
}
as_string = ss.str();
}
void linear_view::lane_order::Custom::cleanup_string() {
as_string.resize(16);
for (auto& c : as_string) {
if (not letter_to_index.contains(c)) {
c = '_';
}
}
}
void linear_view::lane_order::Custom::update_from_string() {
lane_to_button = {{
{}, {}, {}, {},
{}, {}, {}, {},
{}, {}, {}, {},
{}, {}, {}, {}
}};
button_to_lane.clear();
const auto upper_bound = std::min(16UL, as_string.length());
for (std::size_t lane = 0; lane < upper_bound; lane++) {
const auto letter = as_string.at(lane);
const auto pair = letter_to_index.find(letter);
if (pair != letter_to_index.end()) {
const auto button = pair->second;
lane_to_button[lane] = button;
button_to_lane[button] = lane;
}
}
}

View File

@ -0,0 +1,42 @@
#pragma once
#include <array>
#include <map>
#include <optional>
#include <string>
#include <variant>
namespace linear_view {
namespace lane_order {
struct Default {};
struct Vertical {};
struct Custom {
Custom();
explicit Custom(const std::string& as_string);
std::array<std::optional<unsigned int>, 16> lane_to_button;
std::map<unsigned int, unsigned int> button_to_lane;
std::string as_string;
void cleanup_string();
void update_from_string();
void update_from_array();
};
const std::array<std::string, 16> letters = {
"1","2","3","4",
"5","6","7","8",
"9","a","b","c",
"d","e","f","g"
};
const std::map<char, unsigned int> letter_to_index = {
{'1', 0}, {'2', 1}, {'3', 2}, {'4', 3},
{'5', 4}, {'6', 5}, {'7', 6}, {'8', 7},
{'9', 8}, {'a', 9}, {'b', 10}, {'c', 11},
{'d', 12}, {'e', 13}, {'f', 14}, {'g', 15},
};
}
using LaneOrder = std::variant<lane_order::Default, lane_order::Vertical, lane_order::Custom>;
const LaneOrder default_lane_order = lane_order::Default{};
}

View File

@ -29,6 +29,8 @@
#include "../special_numeric_types.hpp"
#include "../toolbox.hpp"
#include "../variant_visitor.hpp"
#include "sizes.hpp"
#include "widgets/lane_order.hpp"
const std::string font_file = "fonts/NotoSans-Medium.ttf";
@ -40,8 +42,9 @@ void SelectionRectangle::reset() {
LinearView::LinearView(std::filesystem::path assets, config::LinearView& config_) :
colors(config_.colors),
sizes(config_.sizes),
beats_to_pixels_proportional(0, 1, 0, 100),
lane_order(LaneOrderPresets::Default{})
lane_order(config_.lane_order)
{}
void LinearView::draw(
@ -57,12 +60,12 @@ void LinearView::draw(
int x = std::max(300, static_cast<int>(size.x));
int y = std::max(300, static_cast<int>(size.y));
const float timeline_width = static_cast<float>(x) - static_cast<float>(timeline_margin);
const float timeline_left = static_cast<float>(timeline_margin) / 2;
const float timeline_width = static_cast<float>(x) - static_cast<float>(sizes.timeline_margin);
const float timeline_left = static_cast<float>(sizes.timeline_margin) / 2;
const float timeline_right = timeline_left + timeline_width;
// Just in case, clamp the beat cursor inside the window, with some margin
const float cursor_y = std::clamp(static_cast<float>(cursor_height), 25.f, static_cast<float>(y) - 25.f);
const float cursor_y = std::clamp(static_cast<float>(sizes.cursor_height), 25.f, static_cast<float>(y) - 25.f);
// Here we compute the range of visible beats from the size of the window
// in pixels, we know by definition that the current beat is exactly at
@ -386,41 +389,34 @@ void LinearView::set_zoom(int newZoom) {
reload_transforms();
}
const std::array<std::string, 16> letters = {
"1","2","3","4",
"5","6","7","8",
"9","a","b","c",
"d","e","f","g"
};
void LinearView::display_settings() {
if (ImGui::Begin("Linear View Settings", &shouldDisplaySettings)) {
if (ImGui::CollapsingHeader("Lanes##Linear View Settings")) {
if (ImGui::BeginCombo("Order", lane_order_name().c_str())) {
if (ImGui::Selectable(
"Default",
std::holds_alternative<LaneOrderPresets::Default>(lane_order)
std::holds_alternative<linear_view::lane_order::Default>(lane_order)
)) {
lane_order = LaneOrderPresets::Default{};
lane_order = linear_view::lane_order::Default{};
}
if (ImGui::Selectable(
"Vertical",
std::holds_alternative<LaneOrderPresets::Vertical>(lane_order)
std::holds_alternative<linear_view::lane_order::Vertical>(lane_order)
)) {
lane_order = LaneOrderPresets::Vertical{};
lane_order = linear_view::lane_order::Vertical{};
}
if (ImGui::Selectable(
"Custom",
std::holds_alternative<CustomLaneOrder>(lane_order)
std::holds_alternative<linear_view::lane_order::Custom>(lane_order)
)) {
if (not std::holds_alternative<CustomLaneOrder>(lane_order)) {
lane_order = CustomLaneOrder{};
if (not std::holds_alternative<linear_view::lane_order::Custom>(lane_order)) {
lane_order = linear_view::lane_order::Custom{};
}
}
ImGui::EndCombo();
}
if (std::holds_alternative<CustomLaneOrder>(lane_order)) {
auto& order = std::get<CustomLaneOrder>(lane_order);
if (std::holds_alternative<linear_view::lane_order::Custom>(lane_order)) {
auto& order = std::get<linear_view::lane_order::Custom>(lane_order);
if (ImGui::InputText("Custom", &order.as_string)) {
order.cleanup_string();
order.update_from_string();
@ -431,7 +427,7 @@ void LinearView::display_settings() {
}
if (ImGui::CollapsingHeader("Colors##Linear View Settings")) {
if (ImGui::Button("Reset##Colors##Linear View Settings")) {
colors = default_linear_view_colors;
colors = linear_view::default_colors;
}
feis::ColorEdit4("Cursor", colors.cursor);
feis::ColorEdit4("Measure Lines", colors.measure_line);
@ -468,8 +464,11 @@ void LinearView::display_settings() {
}
}
if (ImGui::CollapsingHeader("Metrics##Linear View Settings")) {
ImGui::DragInt("Cursor Height", &cursor_height);
ImGui::DragInt("Timeline Margin", &timeline_margin);
if (ImGui::Button("Reset##Metrics##Linear View Settings")) {
sizes = linear_view::default_sizes;
}
ImGui::DragInt("Cursor Height", &sizes.cursor_height);
ImGui::DragInt("Timeline Margin", &sizes.timeline_margin);
}
}
ImGui::End();
@ -486,22 +485,22 @@ void LinearView::reload_transforms() {
std::string LinearView::lane_order_name() {
const auto name = VariantVisitor {
[](LaneOrderPresets::Default) { return "Default"; },
[](LaneOrderPresets::Vertical) { return "Vertical"; },
[](CustomLaneOrder) { return "Custom"; },
[](linear_view::lane_order::Default) { return "Default"; },
[](linear_view::lane_order::Vertical) { return "Vertical"; },
[](linear_view::lane_order::Custom) { return "Custom"; },
};
return std::visit(name, lane_order);
}
std::optional<unsigned int> LinearView::button_to_lane(const better::Position& button) {
const auto _button_to_lane = VariantVisitor {
[button](const LaneOrderPresets::Default&){
[button](const linear_view::lane_order::Default&){
return static_cast<std::optional<unsigned int>>(button.index());
},
[button](const LaneOrderPresets::Vertical&){
[button](const linear_view::lane_order::Vertical&){
return static_cast<std::optional<unsigned int>>(button.get_y() + 4 * button.get_x());
},
[button](const CustomLaneOrder& c){
[button](const linear_view::lane_order::Custom& c){
const auto pair = c.button_to_lane.find(button.index());
if (pair != c.button_to_lane.end()) {
return static_cast<std::optional<unsigned int>>(pair->second);
@ -634,7 +633,7 @@ void LaneOrderPreview(const std::array<std::optional<unsigned int>, 16>& order)
for (auto x = 0; x < 4; x++) {
const auto index = x + 4*y;
ImGui::SetCursorPos(origin + sf::Vector2f{static_cast<float>(x), static_cast<float>(y)} * scale);
ImGui::TextColored(rainbow.at(index), "%s", letters.at(index).c_str());
ImGui::TextColored(rainbow.at(index), "%s", linear_view::lane_order::letters.at(index).c_str());
if (x != 3) {
ImGui::SameLine();
}
@ -651,68 +650,11 @@ void LaneOrderPreview(const std::array<std::optional<unsigned int>, 16>& order)
const auto optional_button = order.at(lane);
if (optional_button) {
const auto button = *optional_button % 16;
ImGui::TextColored(rainbow.at(button), "%s", letters.at(button).c_str());
ImGui::TextColored(rainbow.at(button), "%s", linear_view::lane_order::letters.at(button).c_str());
} else {
ImGui::TextDisabled("_");
}
}
ImGui::SetCursorPos(origin);
ImGui::Dummy(sf::Vector2f{23, 4}*scale);
}
LinearView::CustomLaneOrder::CustomLaneOrder() :
lane_to_button({0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15})
{
update_from_array();
}
void LinearView::CustomLaneOrder::update_from_array() {
std::stringstream ss;
button_to_lane.clear();
for (std::size_t lane = 0; lane < lane_to_button.size(); lane++) {
const auto button = lane_to_button.at(lane);
if (button) {
ss << letters.at(*button);
button_to_lane[*button] = lane;
} else {
ss << "_";
}
}
as_string = ss.str();
}
const std::map<char, unsigned int> letter_to_index = {
{'1', 0}, {'2', 1}, {'3', 2}, {'4', 3},
{'5', 4}, {'6', 5}, {'7', 6}, {'8', 7},
{'9', 8}, {'a', 9}, {'b', 10}, {'c', 11},
{'d', 12}, {'e', 13}, {'f', 14}, {'g', 15},
};
void LinearView::CustomLaneOrder::cleanup_string() {
as_string.resize(16);
for (auto& c : as_string) {
if (not letter_to_index.contains(c)) {
c = '_';
}
}
}
void LinearView::CustomLaneOrder::update_from_string() {
lane_to_button = {{
{}, {}, {}, {},
{}, {}, {}, {},
{}, {}, {}, {},
{}, {}, {}, {}
}};
button_to_lane.clear();
const auto upper_bound = std::min(16UL, as_string.length());
for (std::size_t lane = 0; lane < upper_bound; lane++) {
const auto letter = as_string.at(lane);
const auto pair = letter_to_index.find(letter);
if (pair != letter_to_index.end()) {
const auto button = pair->second;
lane_to_button[lane] = button;
button_to_lane[button] = lane;
}
}
}

View File

@ -1,16 +1,20 @@
#pragma once
#include <filesystem>
#include <imgui.h>
#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/System/Vector2.hpp>
#include <cmath>
#include <filesystem>
#include "../better_timing.hpp"
#include "../chart_state.hpp"
#include "../toolbox.hpp"
#include "config.hpp"
#include "imgui.h"
#include "../colors.hpp"
#include "../config.hpp"
#include "../sizes.hpp"
#include "lane_order.hpp"
struct SelectionRectangle {
sf::Vector2f start = {-1, -1};
@ -43,11 +47,12 @@ public:
void display_settings();
private:
LinearViewColors& colors;
int timeline_margin = 130;
int cursor_height = 100;
private:
linear_view::Colors& colors;
linear_view::Sizes& sizes;
AffineTransform<Fraction> beats_to_pixels_proportional;
void reload_transforms();
@ -58,22 +63,7 @@ private:
bool started_selection_inside_window = false;
bool any_bpm_button_hovered = false;
struct LaneOrderPresets {
struct Default {};
struct Vertical {};
};
struct CustomLaneOrder {
CustomLaneOrder();
std::array<std::optional<unsigned int>, 16> lane_to_button;
std::map<unsigned int, unsigned int> button_to_lane;
std::string as_string;
void cleanup_string();
void update_from_string();
void update_from_array();
};
std::variant<LaneOrderPresets::Default, LaneOrderPresets::Vertical, CustomLaneOrder> lane_order;
linear_view::LaneOrder& lane_order;
std::string lane_order_name();
std::optional<unsigned int> button_to_lane(const better::Position& button);
};

View File

@ -1,5 +1,6 @@
sources += files([
'blank_screen.cpp',
'density_graph.cpp',
'lane_order.cpp',
'linear_view.cpp'
])