cigui/README.md
N0ffie d2ff63b087 Update README.md
Wrong example for file name rule
2025-04-12 20:29:15 +02:00

7 KiB

cigui

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: PascalCase (e.g., GridLayout.hpp)

Classes and Types

  • Class names: PascalCase (e.g., Button, GridLayout)
  • Type aliases/typedefs: lowercase (e.g., using view_ptr = std::shared_ptr<View>)
  • Enum names: PascalCase
  • Enum values: PascalCase/UPPERCASE (e.g., enum class Alignment { TopLeft, Center, BottomRight } or enum class Direction { LEFT, UP, RIGHT, DOWN })

Functions and Variables

  • API function names: camelCase (e.g., getPosition(), setVisible())
  • Internal function names: lowercase (e.g. render_view(), get_visibility())
  • Variable names: camelCase (e.g., buttonText, isVisible)
  • Member variables: prefix with m_ + UpperCamelCase (e.g., m_Position, m_Size)
  • Static variables: prefix with s_ + UpperCamelCase (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
// 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
// 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
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
// 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
/**
 * @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
// 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

#include <cigui.hpp>

struct ContentView : public cig::View {
    void body() {
        cig::VStack(
            cig::SystemIcon("globe.fill"),
            cig::Text("Welcome to Cigui")
        )
    }
}

struct SettingsView : public cig::View {
    void body() {
        cig::Tabbed(
            cig::Tab(

            ).tag("Tab 1"),
            cig::Tab(
                cig::VStack(
                    cig::Text("Other Tab")
                )
            )
        );
    }
}

struct MainApp : public cig::App {
    void body() {
        cig::WindowGroup()(
            cig::ViewWindow(new ContentView()),
            cig::SettingsWindow(new SettingsView())
        )
    }
}