Skip to content

Commit

Permalink
Add underlining as a custom decorator (#6399)
Browse files Browse the repository at this point in the history
* add underlining and corner border custom decorators

* some cleanup

* CLANG
  • Loading branch information
MjnMixael authored Dec 18, 2024
1 parent 9b1d425 commit 7f48375
Show file tree
Hide file tree
Showing 6 changed files with 488 additions and 0 deletions.
248 changes: 248 additions & 0 deletions code/scpui/RocketDecorators.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
#include "RocketDecorators.h"

#include <Rocket/Core/RenderInterface.h>
#include <Rocket/Core/Vertex.h>

namespace scpui {
namespace decorators {

// ================================
// Element Underline Implementation
// ================================

DecoratorUnderline::DecoratorUnderline(float thickness, LineStyle style, float length, float space, Rocket::Core::Colourb color, bool element_color)
: line_thickness(thickness), line_style(style), line_length(length), line_space(space), line_color(color), use_element_color(element_color)
{
}

DecoratorUnderline::~DecoratorUnderline() = default;

// Generate per-element data, if needed (return NULL if not needed)
Rocket::Core::DecoratorDataHandle DecoratorUnderline::GenerateElementData(Rocket::Core::Element* /*element*/)
{
return 0; // No per-element data needed
}

// Release any element-specific data
void DecoratorUnderline::ReleaseElementData(Rocket::Core::DecoratorDataHandle /*element_data*/)
{
// No element-specific data to release in this case
}

void DecoratorUnderline::RenderElement(Rocket::Core::Element* element,
Rocket::Core::DecoratorDataHandle /*element_data*/)
{
// Get the size of the element's content area
Rocket::Core::Vector2f element_size = element->GetBox().GetSize(Rocket::Core::Box::CONTENT);

// Determine line rendering properties based on style
float dash_length = 5.0f;
float space_length = 3.0f;

switch (line_style) {
case LineStyle::Dotted:
dash_length = line_thickness; // Dotted lines are small circles
space_length = line_space;
break;
case LineStyle::Dashed:
space_length = line_space;
dash_length = line_length;
break;
case LineStyle::Solid:
default:
space_length = 0; // No space for solid lines
break;
}

// Calculate the starting position (bottom-left of the content area)
Rocket::Core::Vector2f start = element->GetAbsoluteOffset(Rocket::Core::Box::CONTENT);
float x_position = start.x;
float y_position = start.y + element_size.y - (line_thickness * 2); // Adjusted y-position using the thickness

Rocket::Core::RenderInterface* render_interface = element->GetRenderInterface();

// Create space for vertices and indices for all dashes
std::vector<Rocket::Core::Vertex> vertices;
std::vector<int> indices;

int vertex_count = 0;

Rocket::Core::Colourb render_color = line_color;
if (use_element_color) {
render_color = element->GetProperty<Rocket::Core::Colourb>("color");
}

// Loop through to create each dash as a rectangle (or dot)
while (x_position < start.x + element_size.x) {
float dash_end_x = std::min(x_position + dash_length, start.x + element_size.x);

// Create four vertices for the rectangle (two triangles)
Rocket::Core::Vertex top_left, top_right, bottom_left, bottom_right;

// Set vertex positions
top_left.position = Rocket::Core::Vector2f(x_position, y_position);
top_right.position = Rocket::Core::Vector2f(dash_end_x, y_position);
bottom_left.position = Rocket::Core::Vector2f(x_position, y_position + line_thickness);
bottom_right.position = Rocket::Core::Vector2f(dash_end_x, y_position + line_thickness);

// Set the color
top_left.colour = render_color;
top_right.colour = render_color;
bottom_left.colour = render_color;
bottom_right.colour = render_color;

// Set texture coordinates to 0 since we have no texture
top_left.tex_coord = Rocket::Core::Vector2f(0, 0);
top_right.tex_coord = Rocket::Core::Vector2f(0, 0);
bottom_left.tex_coord = Rocket::Core::Vector2f(0, 0);
bottom_right.tex_coord = Rocket::Core::Vector2f(0, 0);

// Add the vertices to the list
vertices.push_back(top_left);
vertices.push_back(top_right);
vertices.push_back(bottom_left);
vertices.push_back(bottom_right);

// Add indices for two triangles
indices.push_back(vertex_count); // First triangle
indices.push_back(vertex_count + 1);
indices.push_back(vertex_count + 2);

indices.push_back(vertex_count + 1); // Second triangle
indices.push_back(vertex_count + 3);
indices.push_back(vertex_count + 2);

// Move to the next dash position
x_position += dash_length + space_length;

// Increment vertex count for the next set of triangles
vertex_count += 4;
}

// Render the geometry
render_interface->RenderGeometry(vertices.data(),
(int)vertices.size(),
indices.data(),
(int)indices.size(),
0,
Rocket::Core::Vector2f(0, 0));
}

// =============================
// Corner Borders Implementation
// =============================

DecoratorCornerBorders::DecoratorCornerBorders(float thickness, float length_h, float length_v, Rocket::Core::Colourb color)
: border_thickness(thickness), border_length_h(length_h), border_length_v(length_v), border_color(color)
{
}

DecoratorCornerBorders::~DecoratorCornerBorders() = default;

// Generate per-element data, if needed (return NULL if not needed)
Rocket::Core::DecoratorDataHandle DecoratorCornerBorders::GenerateElementData(Rocket::Core::Element* /*element*/)
{
return 0; // No per-element data needed
}

// Release any element-specific data
void DecoratorCornerBorders::ReleaseElementData(Rocket::Core::DecoratorDataHandle /*element_data*/)
{
// No element-specific data to release in this case
}

void DecoratorCornerBorders::RenderElement(Rocket::Core::Element* element,
Rocket::Core::DecoratorDataHandle /*element_data*/)
{
// Get the size of the element's content area
Rocket::Core::Vector2f element_size = element->GetBox().GetSize(Rocket::Core::Box::CONTENT);
Rocket::Core::Vector2f start = element->GetAbsoluteOffset(Rocket::Core::Box::CONTENT);

Rocket::Core::RenderInterface* render_interface = element->GetRenderInterface();

// Create space for vertices and indices for the corner borders
std::vector<Rocket::Core::Vertex> vertices;
std::vector<int> indices;

int vertex_count = 0;

// Define the four corner positions, moved inward by thickness
Rocket::Core::Vector2f top_left = start + Rocket::Core::Vector2f(border_thickness, border_thickness);
Rocket::Core::Vector2f top_right =
start + Rocket::Core::Vector2f(element_size.x - border_thickness, border_thickness);
Rocket::Core::Vector2f bottom_left =
start + Rocket::Core::Vector2f(border_thickness, element_size.y - border_thickness);
Rocket::Core::Vector2f bottom_right =
start + Rocket::Core::Vector2f(element_size.x - border_thickness, element_size.y - border_thickness);

// Function to add a line segment as two triangles
auto add_line_segment = [&](const Rocket::Core::Vector2f& start_pos, const Rocket::Core::Vector2f& end_pos, float thickness) {
// Vector direction of the line
Rocket::Core::Vector2f direction = end_pos - start_pos;
// Normalize direction and calculate the perpendicular vector for thickness
Rocket::Core::Vector2f perpendicular =
Rocket::Core::Vector2f(-direction.y, direction.x).Normalise() * thickness;

// Define the four vertices of the line as a rectangle (two triangles)
Rocket::Core::Vertex v1, v2, v3, v4;
v1.position = start_pos - perpendicular;
v2.position = start_pos + perpendicular;
v3.position = end_pos - perpendicular;
v4.position = end_pos + perpendicular;

// Set color
v1.colour = border_color;
v2.colour = border_color;
v3.colour = border_color;
v4.colour = border_color;

// Add vertices
vertices.push_back(v1);
vertices.push_back(v2);
vertices.push_back(v3);
vertices.push_back(v4);

// Add indices for two triangles
indices.push_back(vertex_count);
indices.push_back(vertex_count + 1);
indices.push_back(vertex_count + 2);

indices.push_back(vertex_count + 1);
indices.push_back(vertex_count + 3);
indices.push_back(vertex_count + 2);

vertex_count += 4;
};

// Define the lengths for horizontal and vertical borders
Rocket::Core::Vector2f horizontal(border_length_h, 0);
Rocket::Core::Vector2f vertical(0, border_length_v);

// Add corner borders by adding two line segments for each corner, moved inward by thickness
// Top-left corner
add_line_segment(top_left, top_left + horizontal, border_thickness); // Horizontal line
add_line_segment(top_left, top_left + vertical, border_thickness); // Vertical line

// Top-right corner
add_line_segment(top_right, top_right - horizontal, border_thickness); // Horizontal line
add_line_segment(top_right, top_right + vertical, border_thickness); // Vertical line

// Bottom-left corner
add_line_segment(bottom_left, bottom_left + horizontal, border_thickness); // Horizontal line
add_line_segment(bottom_left, bottom_left - vertical, border_thickness); // Vertical line

// Bottom-right corner
add_line_segment(bottom_right, bottom_right - horizontal, border_thickness); // Horizontal line
add_line_segment(bottom_right, bottom_right - vertical, border_thickness); // Vertical line

// Render the geometry
render_interface->RenderGeometry(vertices.data(),
(int)vertices.size(),
indices.data(),
(int)indices.size(),
0,
Rocket::Core::Vector2f(0, 0));
}

} // namespace decorators
} // namespace scpui
81 changes: 81 additions & 0 deletions code/scpui/RocketDecorators.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once

// Our Assert conflicts with the definitions inside libRocket
#pragma push_macro("Assert")
#undef Assert

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif

#include <Rocket/Core.h>
#include <Rocket/Core/Decorator.h>
#include <Rocket/Core/Element.h>
#include <Rocket/Core/Geometry.h>
#include <Rocket/Core/GeometryUtilities.h>

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

#pragma pop_macro("Assert")

namespace scpui {
namespace decorators {

enum class LineStyle {
Solid,
Dashed,
Dotted,
Num_styles
};

class DecoratorUnderline : public Rocket::Core::Decorator {
public:
// Constructor to accept properties
DecoratorUnderline(float line_thickness, LineStyle line_style, float line_length, float line_space, Rocket::Core::Colourb line_color, bool use_element_color);
~DecoratorUnderline() override;

// Called to generate per-element data for newly decorated elements.
Rocket::Core::DecoratorDataHandle GenerateElementData(Rocket::Core::Element* element) override;

// Called to release element-specific data.
void ReleaseElementData(Rocket::Core::DecoratorDataHandle element_data) override;

// Called to render the decorator on an element.
void RenderElement(Rocket::Core::Element* element, Rocket::Core::DecoratorDataHandle element_data) override;

private:
float line_thickness;
LineStyle line_style;
float line_length;
float line_space;
Rocket::Core::Colourb line_color;
bool use_element_color;
};

class DecoratorCornerBorders : public Rocket::Core::Decorator {
public:
// Constructor to accept properties
DecoratorCornerBorders(float border_thickness, float border_length_h, float border_length_v, Rocket::Core::Colourb border_color);
~DecoratorCornerBorders() override;

// Called to generate per-element data for newly decorated elements.
Rocket::Core::DecoratorDataHandle GenerateElementData(Rocket::Core::Element* element) override;

// Called to release element-specific data.
void ReleaseElementData(Rocket::Core::DecoratorDataHandle element_data) override;

// Called to render the decorator on an element.
void RenderElement(Rocket::Core::Element* element, Rocket::Core::DecoratorDataHandle element_data) override;

private:
float border_thickness;
float border_length_h;
float border_length_v;
Rocket::Core::Colourb border_color;
};

} // namespace decorators
} // namespace scpui
Loading

0 comments on commit 7f48375

Please sign in to comment.