Major Restructuring
N0ffie switched to mingw (fixing linux warnings)
This commit is contained in:
parent
384c758295
commit
8e0e9ceb7c
26 changed files with 1016 additions and 2921 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -103,6 +103,8 @@ dkms.conf
|
||||||
cmake-**
|
cmake-**
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
|
.fleet
|
||||||
|
.cache
|
||||||
.vs
|
.vs
|
||||||
.vscode
|
.vscode
|
||||||
build
|
build
|
196
CMakeLists.txt
196
CMakeLists.txt
|
@ -1,56 +1,180 @@
|
||||||
cmake_minimum_required(VERSION 3.20)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
project(cigui
|
project(cigui
|
||||||
VERSION 0.0.1
|
VERSION 0.0.1
|
||||||
LANGUAGES CXX)
|
LANGUAGES CXX)
|
||||||
|
|
||||||
|
# Options
|
||||||
|
option(CIGUI_BUILD_SHARED "Build CIGUI as a shared library" ON)
|
||||||
|
option(CIGUI_BUILD_EXAMPLES "Build example applications" ON)
|
||||||
|
option(CIGUI_BUILD_TESTS "Build tests" OFF)
|
||||||
|
option(CIGUI_ENABLE_ASAN "Enable Address Sanitizer (Debug)" OFF)
|
||||||
|
option(CIGUI_ENABLE_UBSAN "Enable Undefined Behavior Sanitizer (Debug)" OFF)
|
||||||
|
option(CIGUI_ENABLE_WARNINGS "Enable additional compiler warnings" ON)
|
||||||
|
|
||||||
|
|
||||||
|
# Set C++ standard
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
# Set debug postfix
|
||||||
|
set(CMAKE_DEBUG_POSTFIX "-d")
|
||||||
|
|
||||||
|
# Configure debug symbols and warnings
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
if(MSVC)
|
||||||
|
add_compile_options(/Zi) # Debug symbols for MSVC
|
||||||
|
if(CIGUI_ENABLE_WARNINGS)
|
||||||
|
add_compile_options(/W4) # Higher warning level
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
add_compile_options(-g) # Debug symbols for GCC/Clang
|
||||||
|
if(CIGUI_ENABLE_WARNINGS)
|
||||||
|
add_compile_options(
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-Wshadow
|
||||||
|
-Wconversion
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Enable sanitizers in debug builds if requested
|
||||||
|
if(CIGUI_ENABLE_ASAN)
|
||||||
|
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
|
||||||
|
add_link_options(-fsanitize=address)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CIGUI_ENABLE_UBSAN)
|
||||||
|
add_compile_options(-fsanitize=undefined)
|
||||||
|
add_link_options(-fsanitize=undefined)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Define export macros
|
||||||
|
if(CIGUI_BUILD_SHARED)
|
||||||
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
if(WIN32)
|
||||||
|
add_definitions(-DCIGUI_DLL)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Include CPM.cmake for dependency management
|
||||||
include(cmake/CPM.cmake)
|
include(cmake/CPM.cmake)
|
||||||
include(cmake/utils.cmake)
|
|
||||||
|
|
||||||
|
# Add SFML dependency using CPM
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME spdlog
|
NAME SFML
|
||||||
GITHUB_REPOSITORY gabime/spdlog
|
|
||||||
VERSION 1.15.2
|
|
||||||
GIT_SHALLOW ON
|
|
||||||
EXCLUDE_FROM_ALL
|
|
||||||
)
|
|
||||||
|
|
||||||
CPMAddPackage(
|
|
||||||
NAME sfml
|
|
||||||
GITHUB_REPOSITORY SFML/SFML
|
GITHUB_REPOSITORY SFML/SFML
|
||||||
GIT_TAG 3.0.0
|
GIT_TAG 3.0.0 # Adjust to actual SFML 3 version/tag
|
||||||
GIT_SHALLOW ON
|
OPTIONS
|
||||||
EXCLUDE_FROM_ALL
|
"SFML_BUILD_AUDIO OFF"
|
||||||
SYSTEM
|
"SFML_BUILD_NETWORK OFF"
|
||||||
|
"CMAKE_DEBUG_POSTFIX -d" # Add debug postfix for debug builds
|
||||||
)
|
)
|
||||||
|
|
||||||
CPMAddPackage(
|
# Generate export macros
|
||||||
NAME nlohmann_json
|
include(GenerateExportHeader)
|
||||||
GITHUB_REPOSITORY nlohmann/json
|
configure_file(
|
||||||
VERSION 3.11.2
|
${CMAKE_CURRENT_SOURCE_DIR}/include/cigui/config.h.in
|
||||||
GIT_SHALLOW ON
|
${CMAKE_CURRENT_BINARY_DIR}/include/cigui/config.h
|
||||||
EXCLUDE_FROM_ALL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PROJECT_SOURCE_NAME "${PROJECT_NAME}_SOURCES")
|
# Define library target
|
||||||
|
if(CIGUI_BUILD_SHARED)
|
||||||
|
add_library(cigui SHARED)
|
||||||
|
else()
|
||||||
|
add_library(cigui STATIC)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_files(ExampleSources src cpp hpp c h cxx hxx)
|
# Enable precompiled headers for faster builds
|
||||||
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.16)
|
||||||
|
target_precompile_headers(cigui PRIVATE
|
||||||
|
<string>
|
||||||
|
<memory>
|
||||||
|
<functional>
|
||||||
|
<SFML/Graphics.hpp>
|
||||||
|
<SFML/Window.hpp>
|
||||||
|
<SFML/System.hpp>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${ExampleSources} cigui.hpp)
|
# Add source files
|
||||||
|
include(cmake/utils.cmake)
|
||||||
|
find_files(CIGUI_SOURCES "src" cpp c cxx)
|
||||||
|
find_files(CIGUI_HEADERS "src" hpp h inl hxx)
|
||||||
|
target_sources(cigui PRIVATE ${CIGUI_SOURCES})
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC include ./)
|
# Configure include directories
|
||||||
|
target_include_directories(cigui
|
||||||
|
PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
# Link with SFML
|
||||||
spdlog::spdlog
|
target_link_libraries(cigui PUBLIC sfml-graphics sfml-window sfml-system)
|
||||||
SFML::Graphics
|
|
||||||
SFML::Window
|
# Define installation
|
||||||
SFML::System
|
include(GNUInstallDirs)
|
||||||
SFML::Audio
|
include(CMakePackageConfigHelpers)
|
||||||
SFML::Network
|
|
||||||
nlohmann_json::nlohmann_json)
|
# Install headers
|
||||||
|
install(
|
||||||
|
DIRECTORY include/cigui/
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cigui
|
||||||
|
FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h"
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/include/cigui/config.h
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cigui
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install libraries
|
||||||
|
install(
|
||||||
|
TARGETS cigui
|
||||||
|
EXPORT cigui-targets
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create and install config files
|
||||||
|
configure_package_config_file(
|
||||||
|
cmake/CIGUIConfig.cmake.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/CIGUIConfig.cmake
|
||||||
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cigui
|
||||||
|
)
|
||||||
|
write_basic_package_version_file(
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/CIGUIConfigVersion.cmake
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
COMPATIBILITY SameMajorVersion
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/CIGUIConfig.cmake
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/CIGUIConfigVersion.cmake
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cigui
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
EXPORT cigui-targets
|
||||||
|
FILE CIGUITargets.cmake
|
||||||
|
NAMESPACE cig::
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cigui
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add examples directory
|
||||||
|
if(CIGUI_BUILD_EXAMPLES)
|
||||||
|
add_subdirectory(examples)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add tests directory
|
||||||
|
if(CIGUI_BUILD_TESTS)
|
||||||
|
include(CTest)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
246
README.md
246
README.md
|
@ -1,6 +1,250 @@
|
||||||
# cigui
|
# cigui
|
||||||
|
|
||||||
# Goal
|
## CIGUI Style Guide
|
||||||
|
|
||||||
|
This document outlines the coding standards and best practices for the CIGUI library. Following these guidelines ensures consistency across the project and makes the codebase more maintainable.
|
||||||
|
|
||||||
|
### General Principles
|
||||||
|
|
||||||
|
- **Clarity over cleverness**: Write code that's easy to understand, not code that's clever.
|
||||||
|
- **Consistency**: Follow established patterns within the codebase.
|
||||||
|
- **Documentation**: Document public APIs thoroughly.
|
||||||
|
- **Testability**: Write code that can be easily tested.
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
|
||||||
|
#### Files
|
||||||
|
|
||||||
|
- Header files: `.hpp` for C++ headers, `.h` for C-compatible headers
|
||||||
|
- Implementation files: `.cpp`
|
||||||
|
- Template implementation files: `.inl`
|
||||||
|
- File names: lowercase with underscores (e.g., `grid_layout.hpp`)
|
||||||
|
|
||||||
|
#### Classes and Types
|
||||||
|
|
||||||
|
- Class names: PascalCase (e.g., `Button`, `GridLayout`)
|
||||||
|
- Type aliases/typedefs: PascalCase (e.g., `using WidgetPtr = std::shared_ptr<View>`)
|
||||||
|
- Enum names: PascalCase
|
||||||
|
- Enum values: PascalCase (e.g., `enum class Alignment { TopLeft, Center, BottomRight }`)
|
||||||
|
|
||||||
|
#### Functions and Variables
|
||||||
|
|
||||||
|
- Function names: camelCase (e.g., `getPosition()`, `setVisible()`)
|
||||||
|
- Variable names: camelCase (e.g., `buttonText`, `isVisible`)
|
||||||
|
- Member variables: prefix with `m_` (e.g., `m_position`, `m_size`)
|
||||||
|
- Static variables: prefix with `s_` (e.g., `s_defaultFont`)
|
||||||
|
- Constants and macros: ALL_CAPS with underscores (e.g., `MAX_WIDGETS`, `CIGUI_API`)
|
||||||
|
|
||||||
|
### Code Structure
|
||||||
|
|
||||||
|
#### Namespaces
|
||||||
|
|
||||||
|
- All library code should be inside the `cig` namespace
|
||||||
|
- Avoid deeply nested namespaces
|
||||||
|
- Do not use `using namespace` in headers
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Good
|
||||||
|
namespace cig {
|
||||||
|
class Button : public View {
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad
|
||||||
|
using namespace sf; // Don't do this in headers
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Headers
|
||||||
|
|
||||||
|
- Always use include guards with project-specific prefix
|
||||||
|
- Order includes as follows:
|
||||||
|
1. Related header
|
||||||
|
2. C++ standard library headers
|
||||||
|
3. Third-party library headers (SFML)
|
||||||
|
4. CIGUI headers
|
||||||
|
- Forward declare classes when possible to reduce dependencies
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Example of a good header structure
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cigui/config.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <SFML/Graphics/Drawable.hpp>
|
||||||
|
#include <cigui/utils/rect.hpp>
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
|
||||||
|
class View; // Forward declaration
|
||||||
|
|
||||||
|
class CIGUI_API Container {
|
||||||
|
// Implementation
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cig
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Classes
|
||||||
|
|
||||||
|
- Separate public, protected, and private sections
|
||||||
|
- Order within sections:
|
||||||
|
1. Constructors/Destructors
|
||||||
|
2. Public methods
|
||||||
|
3. Event callbacks
|
||||||
|
4. Static methods
|
||||||
|
5. Member variables
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Button : public View {
|
||||||
|
public:
|
||||||
|
// Constructors/Destructors
|
||||||
|
Button();
|
||||||
|
explicit Button(const std::string& text);
|
||||||
|
~Button() override;
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
void setText(const std::string& text);
|
||||||
|
const std::string& getText() const;
|
||||||
|
|
||||||
|
// Event dispatcher
|
||||||
|
EventDispatcher<MouseEvent> onClicked;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Protected methods
|
||||||
|
void updateAppearance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private methods
|
||||||
|
void initializeGraphics();
|
||||||
|
|
||||||
|
// Member variables
|
||||||
|
std::string m_text;
|
||||||
|
sf::RectangleShape m_background;
|
||||||
|
bool m_isHovered;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Templates
|
||||||
|
|
||||||
|
- Template implementation should be in `.inl` files
|
||||||
|
- Use explicit instantiation for common types in DLLs
|
||||||
|
- Document template parameters
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// In .hpp file
|
||||||
|
template<typename T>
|
||||||
|
class Container {
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
// Include the implementation
|
||||||
|
#include <cigui/widgets/container.inl>
|
||||||
|
|
||||||
|
// In .inl file
|
||||||
|
template<typename T>
|
||||||
|
Container<T>::Container() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// In .cpp file for explicit instantiation
|
||||||
|
template class Container<int>;
|
||||||
|
template class Container<std::string>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### C++ Features
|
||||||
|
|
||||||
|
- **C++ Standard**: Use C++20 features where appropriate
|
||||||
|
- **Smart Pointers**: Use `std::unique_ptr` for exclusive ownership, `std::shared_ptr` for shared ownership
|
||||||
|
- **Auto**: Use `auto` when the type is obvious or when using iterators
|
||||||
|
- **Range-based for loops**: Prefer over traditional for loops
|
||||||
|
- **Lambdas**: Use for short callbacks and event handlers
|
||||||
|
- **Move Semantics**: Support move operations where appropriate
|
||||||
|
|
||||||
|
### Comments and Documentation
|
||||||
|
|
||||||
|
- Use Doxygen-style comments for public APIs
|
||||||
|
- Comment complex algorithms and non-obvious code
|
||||||
|
- Avoid redundant comments that just repeat the code
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
/**
|
||||||
|
* @brief Creates a button with the specified label text
|
||||||
|
*
|
||||||
|
* @param text The text to display on the button
|
||||||
|
* @param size The size of the button (default: 100x30)
|
||||||
|
*/
|
||||||
|
Button(const std::string& text, const sf::Vector2f& size = {100.f, 30.f});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
- Use exceptions for exceptional cases only
|
||||||
|
- Validate input parameters and handle edge cases
|
||||||
|
- Document error conditions in function comments
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
|
||||||
|
- Prefer automatic memory management with smart pointers
|
||||||
|
- Explicitly define ownership models in documentation
|
||||||
|
- Design with RAII principles (Resource Acquisition Is Initialization)
|
||||||
|
|
||||||
|
### DLL/Shared Library Considerations
|
||||||
|
|
||||||
|
- Use `CIGUI_API` macro for all classes and non-inline functions
|
||||||
|
- Use `CIGUI_TEMPLATE_API` for template classes
|
||||||
|
- Handle template instantiation properly (see Templates section)
|
||||||
|
|
||||||
|
### SFML Integration
|
||||||
|
|
||||||
|
- Wrap SFML types when extending functionality
|
||||||
|
- Use SFML conventions for graphics-related code
|
||||||
|
- Don't expose SFML implementation details in public APIs when avoidable
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- Write unit tests for core functionality
|
||||||
|
- Test edge cases and error conditions
|
||||||
|
- Create interactive examples for UI components
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
|
||||||
|
- Indentation: 4 spaces (no tabs)
|
||||||
|
- Line length: 100 characters maximum
|
||||||
|
- Braces: Open brace on same line, close brace on new line
|
||||||
|
- Space after keywords (if, for, while)
|
||||||
|
- No space after function names
|
||||||
|
- Place * and & with the type, not the variable name
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Good formatting example
|
||||||
|
if (condition) {
|
||||||
|
doSomething();
|
||||||
|
} else {
|
||||||
|
doSomethingElse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPosition(const sf::Vector2f& position) {
|
||||||
|
m_position = position;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
|
|
||||||
|
- Prefer composition over inheritance
|
||||||
|
- Design interfaces that are hard to use incorrectly
|
||||||
|
- Follow the Rule of Five/Zero for class design
|
||||||
|
- Make data members private and provide accessors when needed
|
||||||
|
- Consider performance implications in UI code (avoid work in draw methods)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This style guide is a living document and may evolve as the project grows. When in doubt, maintain consistency with the existing codebase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <cigui.hpp>
|
#include <cigui.hpp>
|
||||||
|
|
291
cigui.hpp
291
cigui.hpp
|
@ -1,291 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <SFML/Graphics.hpp>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define TYPEDEF_VECTOR(NAME, T, N, s) typedef NAME<T> vec##N##s;
|
|
||||||
|
|
||||||
#define TYPEDEF_VECTORS(NAME, N) \
|
|
||||||
typedef NAME<float> vec##N; \
|
|
||||||
TYPEDEF_VECTOR(NAME, float, N, f) \
|
|
||||||
TYPEDEF_VECTOR(NAME, double, N, d) \
|
|
||||||
TYPEDEF_VECTOR(NAME, long double, N, ld) \
|
|
||||||
TYPEDEF_VECTOR(NAME, size_t, N, sz) \
|
|
||||||
TYPEDEF_VECTOR(NAME, int, N, i) \
|
|
||||||
TYPEDEF_VECTOR(NAME, unsigned int, N, u) \
|
|
||||||
TYPEDEF_VECTOR(NAME, short, N, s) \
|
|
||||||
TYPEDEF_VECTOR(NAME, unsigned short, N, us) \
|
|
||||||
TYPEDEF_VECTOR(NAME, long, N, l) \
|
|
||||||
TYPEDEF_VECTOR(NAME, unsigned long, N, ul) \
|
|
||||||
TYPEDEF_VECTOR(NAME, long long, N, ll) \
|
|
||||||
TYPEDEF_VECTOR(NAME, unsigned long long, N, ull)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace cig
|
|
||||||
{
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
template<typename T, size_t growth_scalar = 1, size_t growth_summand = 3>
|
|
||||||
class List
|
|
||||||
{
|
|
||||||
std::unique_ptr<T> m_Data;
|
|
||||||
size_t m_Size = 0;
|
|
||||||
size_t m_Capacity;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void reserve(const size_t capacity)
|
|
||||||
{
|
|
||||||
if (!m_Data)
|
|
||||||
{
|
|
||||||
m_Data = std::unique_ptr<T>(static_cast<T*>(calloc(capacity, sizeof(T))));
|
|
||||||
m_Capacity = capacity;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::unique_ptr<T> newData(static_cast<T*>(calloc(capacity, sizeof(T))));
|
|
||||||
std::copy(m_Data.get(), m_Data.get() + m_Size, newData.get());
|
|
||||||
m_Data = std::move(newData);
|
|
||||||
m_Capacity = capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit List(const size_t capacity = 3) : m_Capacity(capacity) { reserve(capacity); }
|
|
||||||
void own(T* data, const size_t size) { m_Data = data; m_Size = size; }
|
|
||||||
void copy(T* data, const size_t size) {
|
|
||||||
m_Data = std::make_unique<T[]>(size);
|
|
||||||
std::copy(data, data + size, m_Data.get());
|
|
||||||
m_Size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t size() const { return m_Size; }
|
|
||||||
|
|
||||||
T& operator[](size_t index) { return m_Data.get()[index]; }
|
|
||||||
const T& operator[](size_t index) const { return m_Data.get()[index]; }
|
|
||||||
|
|
||||||
void need(const size_t additional_size)
|
|
||||||
{
|
|
||||||
if (m_Size + additional_size > m_Capacity)
|
|
||||||
reserve(m_Capacity + additional_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const { return m_Size == 0; }
|
|
||||||
void clear() { m_Size = 0; }
|
|
||||||
|
|
||||||
void push_back(const T& value)
|
|
||||||
{
|
|
||||||
if (m_Size >= m_Capacity)
|
|
||||||
reserve(m_Capacity * growth_scalar + growth_summand);
|
|
||||||
m_Data.get()[m_Size++] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void emplace_back(Args&&... args)
|
|
||||||
{
|
|
||||||
if (m_Size >= m_Capacity)
|
|
||||||
reserve(m_Capacity * growth_scalar + growth_summand);
|
|
||||||
m_Data.get()[m_Size++] = T(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void expand(const List<T>& other)
|
|
||||||
{
|
|
||||||
need(other.size());
|
|
||||||
std::copy(other.m_Data.get(), other.m_Data.get() + other.size(), m_Data.get() + m_Size);
|
|
||||||
m_Size += other.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void iterate(const std::function<void(T&)>& func)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_Size; i++)
|
|
||||||
func(m_Data.get()[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void iterate(const std::function<void(const T&)>& func) const
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < m_Size; i++)
|
|
||||||
func(m_Data.get()[i]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
union Vector2 {
|
|
||||||
struct { T x, y; };
|
|
||||||
struct { T a, b; };
|
|
||||||
};
|
|
||||||
|
|
||||||
TYPEDEF_VECTORS(Vector2, 2)
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
union Vector3 {
|
|
||||||
struct { T x, y, z; };
|
|
||||||
struct { T r, g, b; };
|
|
||||||
};
|
|
||||||
TYPEDEF_VECTORS(Vector3, 3)
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
union Vector4 {
|
|
||||||
struct { T x, y, z, w; };
|
|
||||||
struct { T r, g, b, a; };
|
|
||||||
struct { T left, top, right, bottom; };
|
|
||||||
};
|
|
||||||
TYPEDEF_VECTORS(Vector4, 4)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RenderCall
|
|
||||||
{
|
|
||||||
std::shared_ptr<sf::Drawable> drawable;
|
|
||||||
sf::RenderStates states;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit RenderCall(const sf::RenderStates& states, sf::Drawable* ptr) : drawable(ptr), states(states)
|
|
||||||
{
|
|
||||||
if (!drawable) { throw std::runtime_error("RenderCall::RenderCall(): Drawable is null"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(sf::RenderTarget& target, const sf::RenderStates& states) const { target.draw(*drawable, states); }
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class LayoutSizes : uint8_t
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Min,
|
|
||||||
Max,
|
|
||||||
Fixed,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class LayoutAlignment : uint8_t
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Top,
|
|
||||||
Bottom,
|
|
||||||
Center,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class LayoutDirection : uint8_t
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
LeftToRight,
|
|
||||||
RightToLeft,
|
|
||||||
TopToBottom,
|
|
||||||
BottomToTop,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class LayoutPosition : uint8_t
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Absolute,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Top,
|
|
||||||
Bottom,
|
|
||||||
Center,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Layout
|
|
||||||
{
|
|
||||||
struct {
|
|
||||||
LayoutSizes rule = LayoutSizes::None;
|
|
||||||
vec2f minSize = {0.f, 0.f};
|
|
||||||
vec2f maxSize = {0.f, 0.f};
|
|
||||||
} size;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
LayoutAlignment rule = LayoutAlignment::None;
|
|
||||||
vec4f padding = {0.f, 0.f, 0.f, 0.f};
|
|
||||||
vec4f margin = {0.f, 0.f, 0.f, 0.f};
|
|
||||||
} alignment;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
LayoutDirection rule = LayoutDirection::None;
|
|
||||||
vec2f spacing = {0.f, 0.f};
|
|
||||||
} direction;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
LayoutPosition rule = LayoutPosition::None;
|
|
||||||
vec2f position = {0.f, 0.f};
|
|
||||||
} position;
|
|
||||||
};
|
|
||||||
|
|
||||||
class View
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
utils::List<RenderCall> m_RenderCalls;
|
|
||||||
Layout m_Layout;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~View() = default;
|
|
||||||
|
|
||||||
[[nodiscard]] const utils::List<RenderCall>& renderCalls() const { return m_RenderCalls; }
|
|
||||||
std::unique_ptr<View> content;
|
|
||||||
|
|
||||||
virtual bool update() { if (content) return content->update(); return false; }
|
|
||||||
|
|
||||||
void draw()
|
|
||||||
{
|
|
||||||
if (!m_RenderCalls.empty()) { m_RenderCalls.clear(); }
|
|
||||||
content = std::unique_ptr<View>(body());
|
|
||||||
if (!content) { return; }
|
|
||||||
content->draw();
|
|
||||||
auto& contentRenderCalls = content->renderCalls();
|
|
||||||
m_RenderCalls.expand(contentRenderCalls);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual View* body() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Rectangle : View
|
|
||||||
{
|
|
||||||
sf::Color m_Color;
|
|
||||||
sf::Color m_BorderColor;
|
|
||||||
float m_BorderThickness = 0;
|
|
||||||
|
|
||||||
Rectangle* setBorderColor(const sf::Color& color) { m_BorderColor = color; return this; }
|
|
||||||
Rectangle* setBorderThickness(float thickness) { m_BorderThickness = thickness; return this; }
|
|
||||||
Rectangle* setColor(const sf::Color& color) { m_Color = color; return this; }
|
|
||||||
Rectangle* setSize(const vec2f& size) { m_Layout.size.minSize = size; return this; }
|
|
||||||
|
|
||||||
View* body() override {
|
|
||||||
auto m_Shape = new sf::RectangleShape(sf::Vector2f{m_Layout.size.minSize.x, m_Layout.size.minSize.y});
|
|
||||||
m_Shape->setFillColor(m_Color);
|
|
||||||
if (m_BorderThickness > 0)
|
|
||||||
{
|
|
||||||
m_Shape->setOutlineThickness(m_BorderThickness);
|
|
||||||
m_Shape->setOutlineColor(m_BorderColor);
|
|
||||||
}
|
|
||||||
m_RenderCalls.emplace_back(sf::RenderStates(), m_Shape);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit Rectangle(const vec2f& size) {
|
|
||||||
m_Layout.size.minSize = size;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class Renderer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit Renderer(View* _view) : view(_view) { view->draw(); }
|
|
||||||
|
|
||||||
std::unique_ptr<View> view;
|
|
||||||
|
|
||||||
void update() const
|
|
||||||
{
|
|
||||||
if (view->update())
|
|
||||||
view->draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render(sf::RenderTarget& target, const sf::RenderStates& states) const
|
|
||||||
{
|
|
||||||
view->renderCalls().iterate([&target, &states](const RenderCall& renderCall) { renderCall.draw(target, states); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
16
cmake/CIGUIConfig.cmake.in
Normal file
16
cmake/CIGUIConfig.cmake.in
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
# Import targets created by CIGUITargets.cmake
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/CIGUITargets.cmake")
|
||||||
|
|
||||||
|
# Ensure SFML is available
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
find_dependency(SFML 3 COMPONENTS graphics window system)
|
||||||
|
|
||||||
|
# Define convenient imported target if it doesn't exist
|
||||||
|
if(NOT TARGET cigui::cigui)
|
||||||
|
add_library(cigui::cigui ALIAS cigui)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check all required components are found
|
||||||
|
check_required_components(cigui)
|
26
examples/CMakeLists.txt
Normal file
26
examples/CMakeLists.txt
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Examples CMakeLists.txt
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
|
# Function to easily add examples
|
||||||
|
function(add_cigui_example NAME)
|
||||||
|
find_files(EXAMPLE_${NAME}_SOURCES "${NAME}/src" cpp c cxx hpp h hxx inl)
|
||||||
|
add_executable(EXAMPLE_${NAME} ${EXAMPLE_${NAME}_SOURCES})
|
||||||
|
target_link_libraries(EXAMPLE_${NAME} PRIVATE cigui)
|
||||||
|
|
||||||
|
set_target_properties(EXAMPLE_${NAME} PROPERTIES OUTPUT_NAME "${NAME}")
|
||||||
|
|
||||||
|
# Copy SFML DLLs to output directory on Windows when building shared
|
||||||
|
if(WIN32 AND CIGUI_BUILD_SHARED)
|
||||||
|
add_custom_command(TARGET EXAMPLE_${NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
$<TARGET_FILE:sfml-graphics>
|
||||||
|
$<TARGET_FILE:sfml-window>
|
||||||
|
$<TARGET_FILE:sfml-system>
|
||||||
|
$<TARGET_FILE:cigui>
|
||||||
|
$<TARGET_FILE_DIR:EXAMPLE_${NAME}>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Basic example
|
||||||
|
add_subdirectory(Full)
|
2
examples/Full/CMakeLists.txt
Normal file
2
examples/Full/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
add_cigui_example(General)
|
||||||
|
add_cigui_example(TicTacToe)
|
|
@ -4,12 +4,13 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <cigui.hpp>
|
#include <cigui/cigui.hpp>
|
||||||
|
#include <cigui/utils/List.hpp>
|
||||||
|
|
||||||
// WIP - not working yet
|
// WIP - not working yet
|
||||||
// Layout unsupported
|
// Layout unsupported
|
||||||
struct HStack final : cig::View {
|
struct HStack final : cig::View {
|
||||||
using Stack = cig::utils::List<View*>;
|
using Stack = cig::List<View*>;
|
||||||
Stack views;
|
Stack views;
|
||||||
|
|
||||||
void append(View* view) { views.push_back(view); }
|
void append(View* view) { views.push_back(view); }
|
||||||
|
@ -21,8 +22,8 @@ struct HStack final : cig::View {
|
||||||
}
|
}
|
||||||
|
|
||||||
View* body() override {
|
View* body() override {
|
||||||
views.iterate([this](View* view) { view->draw(); });
|
views.iterate<>([this](View*& view) { view->draw(); });
|
||||||
views.iterate([this](const View* view) {
|
views.iterate([this](View*& view) {
|
||||||
this->m_RenderCalls.expand(view->renderCalls());
|
this->m_RenderCalls.expand(view->renderCalls());
|
||||||
});
|
});
|
||||||
return nullptr;
|
return nullptr;
|
6
examples/Full/TicTacToe/src/main.cpp
Normal file
6
examples/Full/TicTacToe/src/main.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include <iosteam>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::cout << "Hello World" << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
2589
include/argparse.hpp
2589
include/argparse.hpp
File diff suppressed because it is too large
Load diff
18
include/cigui/cigui.hpp
Normal file
18
include/cigui/cigui.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <cigui/config.h>
|
||||||
|
#include <cigui/core/Renderer.hpp>
|
||||||
|
#include <cigui/core/View.hpp>
|
||||||
|
#include <cigui/views/views.hpp>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
constexpr unsigned int VERSION_MAJOR = CIGUI_VERSION_MAJOR;
|
||||||
|
constexpr unsigned int VERSION_MINOR = CIGUI_VERSION_MINOR;
|
||||||
|
constexpr unsigned int VERSION_PATCH = CIGUI_VERSION_PATCH;
|
||||||
|
|
||||||
|
// Version string
|
||||||
|
constexpr const char *VERSION = CIGUI_VERSION;
|
||||||
|
}
|
37
include/cigui/config.h.in
Normal file
37
include/cigui/config.h.in
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Version information
|
||||||
|
#define CIGUI_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||||
|
#define CIGUI_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||||
|
#define CIGUI_VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||||
|
#define CIGUI_VERSION "@PROJECT_VERSION@"
|
||||||
|
|
||||||
|
// Export macros for DLL/shared library
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#if defined(CIGUI_DLL)
|
||||||
|
#if defined(cigui_EXPORTS) // Set by CMake automatically
|
||||||
|
#define CIGUI_API __declspec(dllexport)
|
||||||
|
#define CIGUI_TEMPLATE
|
||||||
|
#else
|
||||||
|
#define CIGUI_API __declspec(dllimport)
|
||||||
|
#define CIGUI_TEMPLATE extern
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define CIGUI_API
|
||||||
|
#define CIGUI_TEMPLATE
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if defined(CIGUI_DLL) && defined(__GNUC__) && __GNUC__ >= 4
|
||||||
|
#define CIGUI_API __attribute__ ((visibility ("default")))
|
||||||
|
#define CIGUI_TEMPLATE
|
||||||
|
#else
|
||||||
|
#define CIGUI_API
|
||||||
|
#define CIGUI_TEMPLATE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For template classes, we use inline in headers
|
||||||
|
#define CIGUI_TEMPLATE_API
|
||||||
|
|
||||||
|
// Special macro for template instantiations
|
||||||
|
#define CIGUI_TEMPLATE_INST extern template class CIGUI_API
|
64
include/cigui/core/Layout.hpp
Normal file
64
include/cigui/core/Layout.hpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cigui/utils/Vectors.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
enum class LayoutSizes : uint8_t {
|
||||||
|
None = 0,
|
||||||
|
Min,
|
||||||
|
Max,
|
||||||
|
Fixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LayoutAlignment : uint8_t {
|
||||||
|
None = 0,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
Center,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LayoutDirection : uint8_t {
|
||||||
|
None = 0,
|
||||||
|
LeftToRight,
|
||||||
|
RightToLeft,
|
||||||
|
TopToBottom,
|
||||||
|
BottomToTop,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LayoutPosition : uint8_t {
|
||||||
|
None = 0,
|
||||||
|
Absolute,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
Center,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Layout {
|
||||||
|
struct {
|
||||||
|
LayoutSizes rule = LayoutSizes::None;
|
||||||
|
vec2f minSize = {0.f, 0.f};
|
||||||
|
vec2f maxSize = {0.f, 0.f};
|
||||||
|
} size;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
LayoutAlignment rule = LayoutAlignment::None;
|
||||||
|
vec4f padding = {0.f, 0.f, 0.f, 0.f};
|
||||||
|
vec4f margin = {0.f, 0.f, 0.f, 0.f};
|
||||||
|
} alignment;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
LayoutDirection rule = LayoutDirection::None;
|
||||||
|
vec2f spacing = {0.f, 0.f};
|
||||||
|
} direction;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
LayoutPosition rule = LayoutPosition::None;
|
||||||
|
vec2f position = {0.f, 0.f};
|
||||||
|
} position;
|
||||||
|
};
|
||||||
|
}
|
23
include/cigui/core/RenderCall.hpp
Normal file
23
include/cigui/core/RenderCall.hpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sfml/Graphics.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
class RenderCall {
|
||||||
|
std::shared_ptr<sf::Drawable> drawable;
|
||||||
|
sf::RenderStates states;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RenderCall(const sf::RenderStates &rstates, sf::Drawable *ptr) : drawable(ptr), states(rstates) {
|
||||||
|
if (!drawable) {
|
||||||
|
throw std::runtime_error("RenderCall::RenderCall(): Drawable is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(sf::RenderTarget &target, const sf::RenderStates &rstates) const {
|
||||||
|
target.draw(*drawable, rstates);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
29
include/cigui/core/Renderer.hpp
Normal file
29
include/cigui/core/Renderer.hpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SFML/Graphics.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <cigui/core/View.hpp>
|
||||||
|
#include <cigui/core/RenderCall.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
|
||||||
|
class Renderer {
|
||||||
|
public:
|
||||||
|
explicit Renderer(View *_view) : view(_view) { view->draw(); }
|
||||||
|
|
||||||
|
std::unique_ptr<View> view;
|
||||||
|
|
||||||
|
void update() const {
|
||||||
|
if (view->update())
|
||||||
|
view->draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(sf::RenderTarget &target, const sf::RenderStates &states) const {
|
||||||
|
auto lambda = [&target, &states](const RenderCall& renderCall) { renderCall.draw(target, states); };
|
||||||
|
view->renderCalls().iterate(lambda);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
44
include/cigui/core/View.hpp
Normal file
44
include/cigui/core/View.hpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <cigui/utils/List.hpp>
|
||||||
|
#include <cigui/core/Layout.hpp>
|
||||||
|
#include <cigui/core/RenderCall.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
namespace cig
|
||||||
|
{
|
||||||
|
class View {
|
||||||
|
protected:
|
||||||
|
List<RenderCall> m_RenderCalls;
|
||||||
|
Layout m_Layout;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~View() = default;
|
||||||
|
|
||||||
|
[[nodiscard]] const List<RenderCall> &renderCalls() const { return m_RenderCalls; }
|
||||||
|
std::unique_ptr<View> content;
|
||||||
|
|
||||||
|
virtual bool update() {
|
||||||
|
if (content)
|
||||||
|
return content->update();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() {
|
||||||
|
if (!m_RenderCalls.empty()) {
|
||||||
|
m_RenderCalls.clear();
|
||||||
|
}
|
||||||
|
content = std::unique_ptr<View>(body());
|
||||||
|
if (!content) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
content->draw();
|
||||||
|
auto &contentRenderCalls = content->renderCalls();
|
||||||
|
m_RenderCalls.expand(contentRenderCalls);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual View *body() = 0;
|
||||||
|
};
|
||||||
|
}
|
79
include/cigui/utils/List.hpp
Normal file
79
include/cigui/utils/List.hpp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
namespace cig
|
||||||
|
{
|
||||||
|
template <typename T, size_t growth_scalar = 1, size_t growth_summand = 3>
|
||||||
|
class List
|
||||||
|
{
|
||||||
|
std::unique_ptr<T> m_Data;
|
||||||
|
size_t m_Size = 0;
|
||||||
|
size_t m_Capacity;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void reserve(const size_t capacity);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit List(const size_t capacity = 3) : m_Capacity(capacity) { reserve(capacity); }
|
||||||
|
|
||||||
|
void own(T* data, const size_t size)
|
||||||
|
{
|
||||||
|
m_Data = data;
|
||||||
|
m_Size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy(T* data, const size_t size)
|
||||||
|
{
|
||||||
|
m_Data = std::make_unique<T[]>(size);
|
||||||
|
std::copy(data, data + size, m_Data.get());
|
||||||
|
m_Size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] size_t size() const { return m_Size; }
|
||||||
|
|
||||||
|
T& operator[](size_t index) { return m_Data.get()[index]; }
|
||||||
|
|
||||||
|
const T& operator[](size_t index) const { return m_Data.get()[index]; }
|
||||||
|
|
||||||
|
void need(const size_t additional_size)
|
||||||
|
{
|
||||||
|
if (m_Size + additional_size > m_Capacity)
|
||||||
|
reserve(m_Capacity + additional_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const { return m_Size == 0; }
|
||||||
|
|
||||||
|
void clear() { m_Size = 0; }
|
||||||
|
|
||||||
|
void push_back(const T& value)
|
||||||
|
{
|
||||||
|
if (m_Size >= m_Capacity)
|
||||||
|
reserve(m_Capacity * growth_scalar + growth_summand);
|
||||||
|
m_Data.get()[m_Size++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void emplace_back(Args&&... args)
|
||||||
|
{
|
||||||
|
if (m_Size >= m_Capacity)
|
||||||
|
reserve(m_Capacity * growth_scalar + growth_summand);
|
||||||
|
m_Data.get()[m_Size++] = T(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void expand(const List<T>& other)
|
||||||
|
{
|
||||||
|
need(other.size());
|
||||||
|
std::copy(other.m_Data.get(), other.m_Data.get() + other.size(), m_Data.get() + m_Size);
|
||||||
|
m_Size += other.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Lambda>
|
||||||
|
void iterate(Lambda&& lambda) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_Size; i++)
|
||||||
|
lambda(m_Data.get()[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#include <cigui/utils/List.inl>
|
22
include/cigui/utils/List.inl
Normal file
22
include/cigui/utils/List.inl
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
#define __LIST_FUNC_DEFINE__(rtt) \
|
||||||
|
template <typename T, size_t growth_scalar, size_t growth_summand> \
|
||||||
|
rtt List<T, growth_scalar, growth_summand>
|
||||||
|
|
||||||
|
|
||||||
|
namespace cig
|
||||||
|
{
|
||||||
|
__LIST_FUNC_DEFINE__(void)::reserve(const size_t capacity)
|
||||||
|
{
|
||||||
|
if (!m_Data)
|
||||||
|
{
|
||||||
|
m_Data = std::unique_ptr<T>(static_cast<T*>(calloc(capacity, sizeof(T))));
|
||||||
|
m_Capacity = capacity;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::unique_ptr<T> newData(static_cast<T*>(calloc(capacity, sizeof(T))));
|
||||||
|
std::copy(m_Data.get(), m_Data.get() + m_Size, newData.get());
|
||||||
|
m_Data = std::move(newData);
|
||||||
|
m_Capacity = capacity;
|
||||||
|
}
|
||||||
|
}
|
66
include/cigui/utils/Vectors.hpp
Normal file
66
include/cigui/utils/Vectors.hpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define TYPEDEF_VECTOR(NAME, T, N, s) typedef NAME<T> vec##N##s;
|
||||||
|
|
||||||
|
#define TYPEDEF_VECTORS(NAME, N) \
|
||||||
|
typedef NAME<float> vec##N; \
|
||||||
|
TYPEDEF_VECTOR(NAME, float, N, f) \
|
||||||
|
TYPEDEF_VECTOR(NAME, double, N, d) \
|
||||||
|
TYPEDEF_VECTOR(NAME, long double, N, ld) \
|
||||||
|
TYPEDEF_VECTOR(NAME, size_t, N, sz) \
|
||||||
|
TYPEDEF_VECTOR(NAME, int, N, i) \
|
||||||
|
TYPEDEF_VECTOR(NAME, unsigned int, N, u) \
|
||||||
|
TYPEDEF_VECTOR(NAME, short, N, s) \
|
||||||
|
TYPEDEF_VECTOR(NAME, unsigned short, N, us) \
|
||||||
|
TYPEDEF_VECTOR(NAME, long, N, l) \
|
||||||
|
TYPEDEF_VECTOR(NAME, unsigned long, N, ul) \
|
||||||
|
TYPEDEF_VECTOR(NAME, long long, N, ll) \
|
||||||
|
TYPEDEF_VECTOR(NAME, unsigned long long, N, ull)
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define UNNAMED_STRUCT __extension__ struct
|
||||||
|
#else
|
||||||
|
#defien UNNAMED_STRUCT struct
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
template <typename T>
|
||||||
|
union Vector2 {
|
||||||
|
UNNAMED_STRUCT {
|
||||||
|
T x, y;
|
||||||
|
};
|
||||||
|
UNNAMED_STRUCT {
|
||||||
|
T a, b;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
TYPEDEF_VECTORS(Vector2, 2)
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
union Vector3 {
|
||||||
|
UNNAMED_STRUCT {
|
||||||
|
T x, y, z;
|
||||||
|
};
|
||||||
|
UNNAMED_STRUCT {
|
||||||
|
T r, g, b;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
TYPEDEF_VECTORS(Vector3, 3)
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
union Vector4 {
|
||||||
|
UNNAMED_STRUCT {
|
||||||
|
T x, y, z, w;
|
||||||
|
};
|
||||||
|
UNNAMED_STRUCT {
|
||||||
|
T r, g, b, a;
|
||||||
|
};
|
||||||
|
UNNAMED_STRUCT {
|
||||||
|
T left, top, right, bottom;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
TYPEDEF_VECTORS(Vector4, 4)
|
||||||
|
}
|
43
include/cigui/views/Rectangle.hpp
Normal file
43
include/cigui/views/Rectangle.hpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cigui/core/View.hpp>
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
struct Rectangle : View {
|
||||||
|
sf::Color m_Color;
|
||||||
|
sf::Color m_BorderColor;
|
||||||
|
float m_BorderThickness = 0;
|
||||||
|
|
||||||
|
Rectangle *setBorderColor(const sf::Color &color) {
|
||||||
|
m_BorderColor = color;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Rectangle *setBorderThickness(float thickness) {
|
||||||
|
m_BorderThickness = thickness;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Rectangle *setColor(const sf::Color &color) {
|
||||||
|
m_Color = color;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Rectangle *setSize(const vec2f &size) {
|
||||||
|
m_Layout.size.minSize = size;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
View *body() override {
|
||||||
|
auto m_Shape = new sf::RectangleShape(sf::Vector2f{m_Layout.size.minSize.x, m_Layout.size.minSize.y});
|
||||||
|
m_Shape->setFillColor(m_Color);
|
||||||
|
if (m_BorderThickness > 0) {
|
||||||
|
m_Shape->setOutlineThickness(m_BorderThickness);
|
||||||
|
m_Shape->setOutlineColor(m_BorderColor);
|
||||||
|
}
|
||||||
|
m_RenderCalls.emplace_back(sf::RenderStates(), m_Shape);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Rectangle(const vec2f &size) {
|
||||||
|
m_Layout.size.minSize = size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
3
include/cigui/views/views.hpp
Normal file
3
include/cigui/views/views.hpp
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cigui/views/Rectangle.hpp>
|
1
src/core/RenderCall.cpp
Normal file
1
src/core/RenderCall.cpp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
0
src/utils/List.cpp
Normal file
0
src/utils/List.cpp
Normal file
23
src/utils/Vectors.cpp
Normal file
23
src/utils/Vectors.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include <cigui/utils/Vectors.hpp>
|
||||||
|
|
||||||
|
#define VECTOR_TEMPLATE_INSTANTIATION(N, T) \
|
||||||
|
template union Vector##N<T>;
|
||||||
|
|
||||||
|
#define VECTOR_TEMPLATE_IMPLEMENTATION(N) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, int) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, float) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, double)\
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, unsigned int) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, unsigned long) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, long) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, unsigned long long) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, long long) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, short) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, unsigned short) \
|
||||||
|
VECTOR_TEMPLATE_INSTANTIATION(N, bool)
|
||||||
|
|
||||||
|
namespace cig {
|
||||||
|
VECTOR_TEMPLATE_IMPLEMENTATION(2)
|
||||||
|
VECTOR_TEMPLATE_IMPLEMENTATION(3)
|
||||||
|
VECTOR_TEMPLATE_IMPLEMENTATION(4)
|
||||||
|
}
|
88
tests/CMakeLists.txt
Normal file
88
tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
|
# Enable testing
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
# Enable code coverage
|
||||||
|
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
option(CIGUI_CODE_COVERAGE "Enable code coverage reporting" OFF)
|
||||||
|
if(CIGUI_CODE_COVERAGE)
|
||||||
|
target_compile_options(cigui_tests PRIVATE --coverage)
|
||||||
|
target_link_options(cigui_tests PRIVATE --coverage)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add GoogleTest (fetched via CPM)
|
||||||
|
CPMAddPackage(
|
||||||
|
NAME GTest
|
||||||
|
GITHUB_REPOSITORY google/googletest
|
||||||
|
VERSION 1.16.0
|
||||||
|
OPTIONS
|
||||||
|
"INSTALL_GTEST OFF"
|
||||||
|
"gtest_force_shared_crt ON"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Function to easily add test files
|
||||||
|
function(add_cigui_test TEST_NAME)
|
||||||
|
add_executable(${TEST_NAME} ${ARGN})
|
||||||
|
target_link_libraries(${TEST_NAME} PRIVATE cigui gtest gtest_main gmock)
|
||||||
|
target_include_directories(${TEST_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
# Add test to CTest
|
||||||
|
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||||
|
|
||||||
|
# Copy DLLs on Windows when using shared libraries
|
||||||
|
if(WIN32 AND CIGUI_BUILD_SHARED)
|
||||||
|
add_custom_command(TARGET ${TEST_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
$<TARGET_FILE:sfml-graphics>
|
||||||
|
$<TARGET_FILE:sfml-window>
|
||||||
|
$<TARGET_FILE:sfml-system>
|
||||||
|
$<TARGET_FILE:cigui>
|
||||||
|
$<TARGET_FILE_DIR:${TEST_NAME}>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Main test executable
|
||||||
|
add_executable(
|
||||||
|
cigui_tests
|
||||||
|
test_main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link against the library and test framework
|
||||||
|
target_link_libraries(
|
||||||
|
cigui_tests
|
||||||
|
PRIVATE
|
||||||
|
cigui
|
||||||
|
gtest
|
||||||
|
gmock
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include private headers for white-box testing
|
||||||
|
target_include_directories(
|
||||||
|
cigui_tests
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}/src
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add main test to CTest
|
||||||
|
add_test(NAME cigui_tests COMMAND cigui_tests)
|
||||||
|
|
||||||
|
# Copy DLLs on Windows when using shared libraries
|
||||||
|
if(WIN32 AND CIGUI_BUILD_SHARED)
|
||||||
|
add_custom_command(TARGET cigui_tests POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
$<TARGET_FILE:sfml-graphics>
|
||||||
|
$<TARGET_FILE:sfml-window>
|
||||||
|
$<TARGET_FILE:sfml-system>
|
||||||
|
$<TARGET_FILE:cigui>
|
||||||
|
$<TARGET_FILE_DIR:cigui_tests>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Optional: Individual test executables
|
||||||
|
# Uncomment to build separate test executables in addition to the main one
|
||||||
|
# add_cigui_test(widget_test widgets/widget_test.cpp)
|
||||||
|
# add_cigui_test(button_test widgets/button_test.cpp)
|
||||||
|
# add_cigui_test(window_test core/window_test.cpp)
|
14
tests/test_main.cpp
Normal file
14
tests/test_main.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// This main can be used to customize test execution
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
std::cout << "Running CIGUI Library Tests\n";
|
||||||
|
|
||||||
|
// Initialize Google Test
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|
||||||
|
// Run the tests and return the result
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
Loading…
Reference in a new issue