////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 Leo Tenenbaum
// This file is part of GraphColoring.
//
// GraphColoring 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.
//
// GraphColoring 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 GraphColoring. If not, see .
////////////////////////////////////////////////////////////////////////////////
#include "levelselect.hpp"
#include "utils/errors.hpp"
#include "graphcoloring.hpp"
namespace graphcoloring {
const char* const LevelSelect::LEVEL_LISTING_PATH = "saves/level-list.xml";
LevelSelect::LevelSelect(gui::Window* window_,
level_click_callback_t level_click_callback_)
: window(window_), level_click_callback(level_click_callback_)
{
ReadCategories();
window->SetRenderCallback([this](gui::Window*){Render();});
window->SetScrollCallback([this](gui::Window*, GdkScrollDirection d) {
if (d == GDK_SCROLL_UP)
Scroll(-1);
else if (d == GDK_SCROLL_DOWN)
Scroll(+1);
});
MakeLevelButtons();
}
void LevelSelect::Scroll(int direction)
{
int circle_radius =
gui::Size(0,0,LEVEL_CIRCLE_RADIUS,0,&min_dimension_both).X();
lowest_y = 0;
for (auto& button : buttons)
if (button->GetY() > lowest_y)
lowest_y = button->GetY();
if (direction == 1
&& lowest_y < window->GetHeight()-10-2*circle_radius)
return;
scroll_position.y -= direction * 10;
if (scroll_position.y > 0)
scroll_position.y = 0;
}
pugi::xml_object_range
LevelSelect::GetCategories()
{
return category_listing.child("category-listing").children("category");
}
void LevelSelect::ButtonClicked(std::string category_id, std::string level_id)
{
level_click_callback(category_id, level_id);
}
void LevelSelect::MakeLevelButtons()
{
double y = LEVEL_CIRCLE_PADDING;
total_points = 0;
for (pugi::xml_node category : GetCategories())
{
std::string category_id(category.attribute("id").value());
gui::Color color
= gui::colors::FromAttribute(category.attribute("color"));
categories_y_positions[category_id] = y; // Keep track of y position to write text to
y += TEXT_SIZE + LEVEL_CIRCLE_PADDING; // Move past text
double x_spacing = // This formula determines the amount of space between each button
(1-LEVELS_PER_ROW*2*LEVEL_CIRCLE_RADIUS) / (LEVELS_PER_ROW+1)
+ 2*LEVEL_CIRCLE_RADIUS;
double x = x_spacing - 2*LEVEL_CIRCLE_RADIUS;
int i = 1; // Index of level
for (pugi::xml_node level : category.children("level"))
{
std::string level_id(level.attribute("id").value());
total_points += level.attribute("points").as_int(0);
if (x > 1.01-x_spacing) // Move down
{
x = x_spacing - 2*LEVEL_CIRCLE_RADIUS;
y += 2 * LEVEL_CIRCLE_RADIUS + LEVEL_CIRCLE_PADDING;
}
bool completed = !level.attribute("completed").empty();
auto button = std::make_unique(window,
std::to_string(i),
gui::Position(0,0,x,y,&min_dimension_y,&scroll_position),
gui::Size(0,0,LEVEL_CIRCLE_RADIUS,0,&min_dimension_both),
completed ? gui::colors::Shade(color, COMPLETED_SHADE) : color,
gui::Alignment::LEFT,
gui::Alignment::TOP, gui::Button::Shape::CIRCLE);
button->SetCommand([category_id, level_id, this]() {
ButtonClicked(category_id, level_id);
});
buttons.push_back(std::move(button));
x += x_spacing;
i++;
}
y += 2 * LEVEL_CIRCLE_RADIUS + LEVEL_CIRCLE_PADDING;
}
}
void LevelSelect::ReadCategories()
{
if (!category_listing.load_file(LEVEL_LISTING_PATH))
utils::errors::Die("Failed to load category listing file.");
}
void LevelSelect::Render()
{
// Update coordinates
int m = std::min(window->GetWidth(), window->GetHeight());
min_dimension_both.SetPos(m,m);
min_dimension_y.SetPos(window->GetWidth(),m);
window->SetDrawColor(GraphColoring::BACKGROUND_COLOR);
window->Clear();
// Draw Buttons
for (std::unique_ptr& button : buttons)
button->Render();
RenderCategoryLabels();
window->SetDrawColor(0xDDDDDDFF);
window->DrawText(std::string("Points: ") + std::to_string(total_points),
gui::Position(window->GetWidth()-10, 10),
gui::Alignment::RIGHT, gui::Alignment::TOP);
}
void LevelSelect::RenderCategoryLabels()
{
window->SetTextSize(TEXT_SIZE*min_dimension_both.X());
for (pugi::xml_node category : GetCategories())
{
std::string id(category.attribute("id").value());
std::string category_name = category.attribute("name").value();
gui::Color color
= gui::colors::FromAttribute(category.attribute("color"));
window->SetDrawColor(color);
double y_pos = categories_y_positions[id];
window->DrawText(category_name,
gui::Position(0,0, LEVEL_CIRCLE_PADDING,y_pos,&min_dimension_both,
&scroll_position),
gui::Alignment::LEFT, gui::Alignment::TOP);
}
}
} // namespace graphcoloring