diff --git a/.clang-format b/.clang-format deleted file mode 100644 index b8060e9..0000000 --- a/.clang-format +++ /dev/null @@ -1,204 +0,0 @@ ---- -BasedOnStyle: Microsoft -AccessModifierOffset: -4 -AlignAfterOpenBracket: DontAlign -AlignArrayOfStructures: None -AlignConsecutiveAssignments: None -AlignConsecutiveBitFields: None -AlignConsecutiveDeclarations: None -AlignConsecutiveMacros: None -AlignEscapedNewlines: Right -AlignOperands: DontAlign -AlignTrailingComments: - Kind: Always - OverEmptyLines: 0 -AllowAllArgumentsOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortEnumsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: MultiLine -AttributeMacros: - - __capability -BinPackArguments: true -BinPackParameters: true -BitFieldColonSpacing: Both -BreakAfterAttributes: Always -BreakAfterJavaFieldAnnotations: false -BreakArrays: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -BraceWrapping: - AfterCaseLabel: false - AfterClass: false - AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false -BreakBeforeConceptDeclarations: Always -BreakBeforeInlineASMColon: OnlyMultiline -BreakBeforeTernaryOperators: true -BreakConstructorInitializers: BeforeComma -BreakInheritanceList: BeforeColon -BreakStringLiterals: true -ColumnLimit: 0 -CommentPragmas: "^ IWYU pragma:" -CompactNamespaces: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: LogicalBlock -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: true -ForEachMacros: - - foreach - - Q_FOREACH - - BOOST_FOREACH -IfMacros: - - KJ_IF_MAYBE -IncludeBlocks: Preserve -IncludeCategories: - - Regex: ^"(llvm|llvm-c|clang|clang-c)/ - Priority: 2 - SortPriority: 0 - CaseSensitive: false - - Regex: ^(<|"(gtest|gmock|isl|json)/) - Priority: 3 - SortPriority: 0 - CaseSensitive: false - - Regex: .* - Priority: 1 - SortPriority: 0 - CaseSensitive: false -IncludeIsMainRegex: (Test)?$ -IncludeIsMainSourceRegex: "" -IndentAccessModifiers: false -IndentCaseBlocks: false -IndentCaseLabels: false -IndentExternBlock: AfterExternBlock -IndentGotoLabels: true -IndentPPDirectives: None -IndentRequiresClause: true -IndentWidth: 4 -IndentWrappedFunctionNames: false -InsertBraces: true -InsertNewlineAtEOF: true -InsertTrailingCommas: None -IntegerLiteralSeparator: - Binary: 0 - BinaryMinDigits: 0 - Decimal: 0 - DecimalMinDigits: 0 - Hex: 0 - HexMinDigits: 0 -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -LambdaBodyIndentation: Signature -Language: Cpp -LineEnding: DeriveLF -MacroBlockBegin: "" -MacroBlockEnd: "" -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: None -ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 4 -ObjCBreakBeforeNestedBlockParam: true -ObjCSpaceAfterProperty: false -ObjCSpaceBeforeProtocolList: true -PPIndentWidth: -1 -PackConstructorInitializers: BinPack -PenaltyBreakAssignment: 2 -PenaltyBreakBeforeFirstCallParameter: 19 -PenaltyBreakComment: 300 -PenaltyBreakFirstLessLess: 120 -PenaltyBreakOpenParenthesis: 0 -PenaltyBreakString: 1000 -PenaltyBreakTemplateDeclaration: 10 -PenaltyExcessCharacter: 1000000 -PenaltyIndentedWhitespace: 0 -PenaltyReturnTypeOnItsOwnLine: 1000 -PointerAlignment: Right -QualifierAlignment: Leave -ReferenceAlignment: Pointer -ReflowComments: true -RemoveBracesLLVM: false -RemoveSemicolon: false -RequiresClausePosition: OwnLine -RequiresExpressionIndentation: OuterScope -SeparateDefinitionBlocks: Leave -ShortNamespaceLines: 1 -SortIncludes: CaseSensitive -SortJavaStaticImport: Before -SortUsingDeclarations: LexicographicNumeric -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceAroundPointerQualifiers: Default -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -SpaceBeforeCpp11BracedList: false -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: Never -SpaceBeforeParensOptions: - AfterControlStatements: true - AfterForeachMacros: true - AfterFunctionDeclarationName: false - AfterFunctionDefinitionName: false - AfterIfMacros: false - AfterOverloadedOperator: false - AfterRequiresInClause: false - AfterRequiresInExpression: false - BeforeNonEmptyParentheses: false -SpaceBeforeRangeBasedForLoopColon: true -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: Never -SpacesInCStyleCastParentheses: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: true -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Latest -StatementAttributeLikeMacros: - - Q_EMIT -StatementMacros: - - Q_UNUSED - - QT_REQUIRE_VERSION -TabWidth: 4 -UseTab: Always -WhitespaceSensitiveMacros: - - BOOST_PP_STRINGIZE - - CF_SWIFT_NAME - - NS_SWIFT_NAME - - PP_STRINGIZE - - STRINGIZE -UseCRLF: false diff --git a/.clangd b/.clangd index 5b3daa0..9734fce 100644 --- a/.clangd +++ b/.clangd @@ -1,2 +1,2 @@ CompileFlags: - Add: [ -Ivendor/, -Ivendor/SDL3/include, -Ivendor/SDL3_ttf/include ] + Add: [ -Wall, --std=c23, -xc, -Ivendor/ ] diff --git a/.dir-locals.el b/.dir-locals.el deleted file mode 100644 index ac91ce2..0000000 --- a/.dir-locals.el +++ /dev/null @@ -1,5 +0,0 @@ -((nil - (indent-tabs-mode . t) - (tab-width . 4) - (lsp-enable-on-type-formatting nil))) -((c-mode . ((mode . c++)))) diff --git a/.gitignore b/.gitignore index e2ff821..85942ff 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,3 @@ build/ Makefile .cache/ compile_commands.json -.kdev4 -game-of-life.kdev4 diff --git a/.gitmodules b/.gitmodules index cdaf7b9..50fb0d4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,13 +1,3 @@ -[submodule "vendor/SDL3"] - path = vendor/SDL3 - url = https://github.com/libsdl-org/SDL.git -[submodule "vendor/SDL3_ttf"] - path = vendor/SDL3_ttf - url = https://github.com/libsdl-org/SDL_ttf.git -[submodule "vendor/SDL3_image"] - path = vendor/SDL3_image - url = https://github.com/libsdl-org/SDL_image.git -[submodule "vendor/ceramic"] - path = vendor/ceramic - url = forgejo@git.objectionable.solutions:Sara/clay-ceramic.git - branch = main +[submodule "vendor/clay"] + path = vendor/clay + url = https://git.objectionable.solutions/Sara/clay.git diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 12e11f3..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -project(dice-gui) - -set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/bin") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") - -set(CMAKE_CXX_STANDARD 23) -set(CMAKE_C_STANDARD 23) - -file(GLOB_RECURSE source_files . src/**.cpp src/**.c vendor/renderer/**.c vendor/ceramic/**.cpp) -include_directories(vendor/) - - -set(SDL_VENDORED ON) -add_subdirectory(vendor/SDL3/ EXCLUDE_FROM_ALL) -set(SDLTTF_VENDORED ON) -add_subdirectory(vendor/SDL3_ttf EXCLUDE_FROM_ALL) -set(SDLIMAGE_AVIF OFF) -set(SDLIMAGE_BMP OFF) -set(SDLIMAGE_TIF OFF) -set(SDLIMAGE_WEBP OFF) -set(SDLIMAGE_VENDORED ON) -add_subdirectory(vendor/SDL3_image EXCLUDE_FROM_ALL) - -add_executable(dice-gui ${source_files}) -target_link_libraries(dice-gui PRIVATE - SDL3_ttf::SDL3_ttf - SDL3_image::SDL3_image - SDL3::SDL3) - -add_custom_target(copy_assets - COMMAND ${CMAKE_COMMAND} -E - copy_directory ${CMAKE_SOURCE_DIR}/assets/ ${CMAKE_BINARY_DIR}/assets -) -add_dependencies(dice-gui copy_assets) diff --git a/README.md b/README.md deleted file mode 100644 index 85b3081..0000000 --- a/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Dice GUI - -A simple dice rolling GUI app using Clay for layout, Ceramic elements and SDL3 rendering. - -# Compiling - -Either `git clone --recursive` or remember to `git submodule update --init --recursive`. Then `cmake -S. -Bbuild` and `cmake --build build`. Which compiles `bin/dice-gui`, which you can run. - -# Code Standards - -* Keep program structure as simple as possible. No `class Application` or other Java-isms. Prefer namespaces with static lifetime variables. - -* Use STL where possible. Don't reinvent the wheel. - -* K&R brackets. With notable exceptions(1) - -* camelCase for variables, PascalCase for types and functions (that's what SDL and Clay do). - -* In class member functions, always use `this->` to access member variables. - -* `const` applies to the name to it's left, so it goes after the type, not `const int x;` but `int const x`. - -* \* and & flush with the declaration name. `Type const &Function(Type &inRef)` - -> (1) Bracket exceptions: -> * using scoped_lock in arbitrary blocks, prefer tailed lisp brackets - -```cpp -struct Data { - int x{ 0 }, y{ 0 }; -}; -void MyFunction(Data const &data) { // K&R here - DoAsynchronousThings(); - { std::scoped_lock lock{ myMutex }; - myVariable++; - } // not quite lisp, lisp with a tail - DoMoreAsynchronousThings(); -} -``` diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..32058f2 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,11 @@ +Roll modifier editor + +Button to roll the active set + +Design representation of internal state of initiative tracker + +x Design representation of internal state of dice tray + +x Buttons for adding dice to active set + +x Buttons for removing dice from active set diff --git a/vendor/clay/clay.h b/include/clay/clay.h similarity index 100% rename from vendor/clay/clay.h rename to include/clay/clay.h diff --git a/justfile b/justfile index 7c23837..19f869e 100644 --- a/justfile +++ b/justfile @@ -1,19 +1,14 @@ build: # BUILDING - cmake --build build + make run: - cd bin/ && dice-gui + bin/DiceGui configure: # CONFIGURING WITH PREMAKE - cmake -S. -Bbuild -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + premake5 gmake clean: - # CLEANING BUILD ARTEFACTS rm -r bin/** rm -r build/** - -set-project-name projectname: clean - git remote set-url origin "" - sed -i "s/dice-gui/{{projectname}}/g" ./CMakeLists.txt ./justfile diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 0000000..193f93a --- /dev/null +++ b/premake5.lua @@ -0,0 +1,23 @@ +workspace "DiceGui" + configurations { "debug", "release" } + location "." + +project "Dice" + kind "ConsoleApp" + language "C" + cdialect "c23" + location "build/" + files { "src/**.c" } + includedirs { "include/" } + links { "m", "stdc++", "SDL3", "SDL3_ttf", "SDL3_image" } + buildoptions { "-Wall" } + targetdir "bin/" + postbuildcommands { "{RMDIR} %{cfg.targetdir}/assets", "{COPYDIR} %{wks.location}/assets/ %{cfg.targetdir}/assets/" } + filter "configurations:debug" + defines { "DEBUG" } + symbols "On" + optimize "Off" + filter "configurations:release" + defines { "NDEBUG" } + optimize "On" + symbols "Off" diff --git a/src/application.c b/src/application.c new file mode 100644 index 0000000..b4b3cc8 --- /dev/null +++ b/src/application.c @@ -0,0 +1,47 @@ +#include "application.h" +#include "dice_container.h" +#include "style.h" +#include +#include + +static inline +void DiceLogContainer() { + CLAY(CLAY_ID("LogContainer"), { + .layout = { + .sizing = layoutExpand, + .padding = CLAY_PADDING_ALL(16), + }, + .PANEL(0), + }) {} +} + +static inline +void InitiativeListContainer() { + CLAY(CLAY_ID("InitiativeListContainer"), { + .layout = { + .sizing = layoutExpand, + .padding = CLAY_PADDING_ALL(16), + }, + .PANEL(0) + }) {} +} + +Clay_RenderCommandArray RenderApplication() { + Clay_BeginLayout(); + CLAY(CLAY_ID("OuterContainer"), WindowStyle()) { + DiceContainer(); + CLAY(CLAY_ID("LowerSplitContainer"), { + .layout = { + .sizing = layoutExpand, + .childGap = containerGap + }, + }) { + DiceLogContainer(); + InitiativeListContainer(); + } + } + return Clay_EndLayout(); +} + +void HandleEvent(SDL_Event event) { +} diff --git a/src/application.cpp b/src/application.cpp deleted file mode 100644 index 130134e..0000000 --- a/src/application.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "application.h" -#include "dice_gui.h" -#include -#include -#include -#include - -constexpr Clay_LayoutConfig centerContainer { - .sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, - .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER}, -}; - -namespace application { -Clay_RenderCommandArray RenderApplication() { - Clay_BeginLayout(); - CLAY(CLAY_ID("Window"), cera::Window()) { - CLAY(CLAY_ID("Content"), {.layout = centerContainer}) { - DiceContainer(); - } - } - return Clay_EndLayout(); -} - -void HandleEvent(SDL_Event event) { -} -} diff --git a/src/application.h b/src/application.h index 9d74ea0..0dce16d 100644 --- a/src/application.h +++ b/src/application.h @@ -4,9 +4,7 @@ #include #include -namespace application { -Clay_RenderCommandArray RenderApplication(); -void HandleEvent(SDL_Event event); -} +extern Clay_RenderCommandArray RenderApplication(); +extern void HandleEvent(SDL_Event evt); #endif // !APPLICATION_H diff --git a/src/defs.h b/src/defs.h new file mode 100644 index 0000000..8349ec7 --- /dev/null +++ b/src/defs.h @@ -0,0 +1,13 @@ +#ifndef DEFS_H +#define DEFS_H + +#include + +#include +#include + + +extern SDL_Window *window; +extern SDL_Renderer *renderer; + +#endif // !DEFS_H diff --git a/src/dice.c b/src/dice.c new file mode 100644 index 0000000..f9e47c0 --- /dev/null +++ b/src/dice.c @@ -0,0 +1,109 @@ +#include "dice.h" +#include + +static int activeDiceCount = 0; +static enum Dice_Die activeDice[MAX_ACTIVE_DICE]; + +static struct Dice_ResultType rollResult[MAX_ACTIVE_DICE]; +static struct Dice_ResultType rollTotal = { + .string = "0", + .roll = 0, .string_len = 1, +}; + +int Dice_Roll(enum Dice_Die die) { + if (die == COIN) { + return (rand() % 2); + } else { + int const max = die; + return (rand() % max) + 1; + } +} + +static +struct Dice_ResultType Dice_RollToResultType(int roll, enum Dice_Die die) { + struct Dice_ResultType result = { }; + result.roll = roll; + if (die == COIN) { + result.string_len = SDL_snprintf(result.string, MAX_ROLL_STR_LEN, roll == 1 ? "H" : "T"); + } else { + result.string_len = SDL_snprintf(result.string, MAX_ROLL_STR_LEN, "%d", roll); + } + result.clay_string = (Clay_String) { + .chars = result.string, + .length = result.string_len, + .isStaticallyAllocated = false + }; + return result; +} + +enum Dice_Die const *Dice_GetActiveSet(size_t *out_length) { + if (out_length != nullptr) { + *out_length = activeDiceCount; + } + return activeDice; +} + +size_t Dice_AddToActiveSet(enum Dice_Die die) { + if (activeDiceCount >= MAX_ACTIVE_DICE) { + return MAX_ACTIVE_DICE; + } + activeDice[activeDiceCount] = die; + rollResult[activeDiceCount] = Dice_RollToResultType(die, die); + rollTotal.roll += die; + rollTotal = Dice_RollToResultType(rollTotal.roll, 0); + return activeDiceCount++; +} + +void Dice_RemoveFromActiveSet(size_t index) { + if (index >= MAX_ACTIVE_DICE) { + return; + } + rollTotal.roll -= rollResult[index].roll; + rollTotal = Dice_RollToResultType(rollTotal.roll, 0); + memcpy(activeDice + index, activeDice + index + 1, MAX_ACTIVE_DICE - index - 1); + --activeDiceCount; +} + +void Dice_ClearActiveSet() { + rollTotal.roll = 0; + rollTotal = Dice_RollToResultType(rollTotal.roll, 0); + activeDiceCount = 0; +} + +void Dice_RollActiveSet() { + int total = 0; + for (size_t i = 0; i < activeDiceCount; ++i) { + rollResult[i] = Dice_RollToResultType(Dice_Roll(activeDice[i]), activeDice[i]); + total += rollResult[i].roll; + } + rollTotal = Dice_RollToResultType(total, 0); +} + +struct Dice_ResultType *Dice_GetLastResult(size_t *out_length) { + if (out_length != nullptr) { + *out_length = activeDiceCount; + } + return rollResult; +} + +struct Dice_ResultType *Dice_GetLastResultTotal() { + rollTotal.clay_string = (Clay_String) { + .chars = rollTotal.string, + .length = rollTotal.string_len, + .isStaticallyAllocated = false + }; + return &rollTotal; +} + +Clay_String Dice_ToString(enum Dice_Die die) { + switch (die) { + case COIN: return CLAY_STRING("C"); + case D4: return CLAY_STRING("4"); + case D6: return CLAY_STRING("6"); + case D8: return CLAY_STRING("8"); + case D10: return CLAY_STRING("10"); + case D12: return CLAY_STRING("12"); + case D20: return CLAY_STRING("20"); + case D100: return CLAY_STRING("100"); + } +} diff --git a/src/dice.h b/src/dice.h new file mode 100644 index 0000000..45405ad --- /dev/null +++ b/src/dice.h @@ -0,0 +1,46 @@ +#ifndef DICE_H +#define DICE_H + +#include "renderer/clay_renderer_SDL3.h" +#include + +enum Dice_Die { + COIN = 1, + D4 = 4, + D6 = 6, + D8 = 8, + D10 = 10, + D12 = 12, + D20 = 20, + D100 = 100 +}; + +#ifndef MAX_ROLL_STR_LEN +#define MAX_ROLL_STR_LEN 10 +#endif +struct Dice_ResultType { + int roll; + size_t string_len; + char string[MAX_ROLL_STR_LEN]; + Clay_String clay_string; +}; + +#ifndef MAX_ACTIVE_DICE +#define MAX_ACTIVE_DICE 20 +#endif + +extern int Dice_Roll(enum Dice_Die die); + +extern enum Dice_Die const *Dice_GetActiveSet(size_t *out_length); +extern size_t Dice_AddToActiveSet(enum Dice_Die die); +extern void Dice_RemoveFromActiveSet(size_t index); +extern void Dice_ClearActiveSet(); + +extern struct Dice_ResultType *Dice_GetLastResult(size_t *out_length); +extern struct Dice_ResultType *Dice_GetLastResultTotal(); + +extern void Dice_RollActiveSet(); + +extern Clay_String Dice_ToString(enum Dice_Die die); + +#endif // !DICE_H diff --git a/src/dice_container.c b/src/dice_container.c new file mode 100644 index 0000000..d73f874 --- /dev/null +++ b/src/dice_container.c @@ -0,0 +1,170 @@ +#include "dice_container.h" +#include +#include +#include +#include +#include "elements.h" +#include "style.h" +#include "dice.h" + +static +void HandleRollSetButtonInteraction(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) { + if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + Dice_RollActiveSet(); + } +} + +static +void HandleClearSetButtonInteraction(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) { + if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + Dice_ClearActiveSet(); + } +} + +static +void HandleAddDieButtonInteraction(Clay_ElementId element, Clay_PointerData pointer, intptr_t die) { + if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + Dice_AddToActiveSet((enum Dice_Die)die); + } +} + +static inline +void AddDieButton(enum Dice_Die die) { + CLAY(CLAY_IDI("AddDieButton", die), { + .layout = { + .sizing = { CLAY_SIZING_FIXED(100), CLAY_SIZING_FIXED(100) }, + .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }, + }, + .image = { GetDiceImage(die, Clay_Hovered()) }, + }) { + Clay_OnHover(&HandleAddDieButtonInteraction, die); + CLAY_TEXT(Dice_ToString(die), CLAY_TEXT_CONFIG({ + .H(2), + .textColor = TextColors(0), + .textAlignment = CLAY_TEXT_ALIGN_CENTER, + })); + } +} + +static inline +void DiceSelectorContainer() { + CLAY(CLAY_ID("DiceSelector"), { + .layout = { + .sizing = { CLAY_SIZING_FIT(), CLAY_SIZING_GROW() }, + }, + .PANEL(0), + }) { + CLAY_AUTO_ID({ + .layout = { + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, + .padding = { 2, 2, 5, 5 }, + .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_TOP }, + }, + .clip = { + false, true, Clay_GetScrollOffset(), + }, + .LISTCONTAINER(0), + }) { + AddDieButton(D4); + AddDieButton(D6); + AddDieButton(D8); + AddDieButton(D10); + AddDieButton(D12); + AddDieButton(D20); + AddDieButton(D100); + AddDieButton(COIN); + } + } +} + +static +void HandleRemoveDieButtonInteraction(Clay_ElementId element, Clay_PointerData pointer, intptr_t index) { + if (pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { + Dice_RemoveFromActiveSet(index); + } +} + +static inline +void RemoveDieButton(enum Dice_Die die, int index) { + CLAY(CLAY_IDI("RemoveDieButton", index), { + .layout = { + .sizing = { CLAY_SIZING_FIXED(200), CLAY_SIZING_FIXED(200) }, + .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }, + }, + .image = { GetDiceImage(die, Clay_Hovered()) }, + }) { + size_t result_length; + struct Dice_ResultType const *result = Dice_GetLastResult(&result_length); + Clay_String string = { + .chars = result[index].string, + .length = result[index].string_len, + .isStaticallyAllocated = true + }; + Clay_OnHover(&HandleRemoveDieButtonInteraction, index); + CLAY_TEXT(string, CLAY_TEXT_CONFIG({ + .H(1), + .textColor = TextColors(0), + .textAlignment = CLAY_TEXT_ALIGN_CENTER, + })); + } +} + +static inline +void ActiveDiceContainer() { + CLAY(CLAY_ID("ActiveDice"), { + .layout = { + .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }, + }, + .PANEL(0), + }) { + CLAY(CLAY_ID("ActiveDiceInner"), { + .layout = { + .sizing = { CLAY_SIZING_FIT(), CLAY_SIZING_GROW() }, + .layoutDirection = CLAY_LEFT_TO_RIGHT, + .childAlignment = { CLAY_ALIGN_X_LEFT, CLAY_ALIGN_Y_CENTER }, + .childGap = 16, + .padding = { 100, 100, 0, 0 }, + }, + .clip = { + true, true, { Clay_GetScrollOffset().x, 0 } + } + }) { + size_t dice_count = 0; + enum Dice_Die const *dice = Dice_GetActiveSet(&dice_count); + for (size_t i = 0; i < dice_count; ++i) { + RemoveDieButton(dice[i], i); + } + } + CLAY(CLAY_ID("ActiveDiceControls"), { + .layout = { + .layoutDirection = CLAY_LEFT_TO_RIGHT, + .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }, + .childGap = 20, + .padding = { 0, 0, 0, 10 }, + }, + }) { + TextButton(CLAY_STRING("Roll"), proceedButton, &HandleRollSetButtonInteraction, 0); + CLAY_TEXT(Dice_GetLastResultTotal()->clay_string, CLAY_TEXT_CONFIG({ + .H(3), + .textColor = TextColors(0), + })); + TextButton(CLAY_STRING("Clear"), warningButton, &HandleClearSetButtonInteraction, 0); + } + } +} + +void DiceContainer() { + CLAY(CLAY_ID("DiceContainer"), { + .layout = { + .layoutDirection = CLAY_LEFT_TO_RIGHT, + .sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_PERCENT(0.4) }, + .childGap = containerGap + }, + }) { + DiceSelectorContainer(); + ActiveDiceContainer(); + } +} diff --git a/src/dice_container.h b/src/dice_container.h new file mode 100644 index 0000000..0be5062 --- /dev/null +++ b/src/dice_container.h @@ -0,0 +1,6 @@ +#ifndef DICE_CONTAINER_H +#define DICE_CONTAINER_H + +extern void DiceContainer(); + +#endif // !DICE_CONTAINER_H diff --git a/src/dice_data.cpp b/src/dice_data.cpp deleted file mode 100644 index 2c014c0..0000000 --- a/src/dice_data.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "dice_data.h" -#include -#include -#include -#include - -namespace active_dice { -struct DieState { - Die type; - uint8_t value; -}; -constexpr size_t maxActiveDice{16}; -DieState activeDice[maxActiveDice]; -size_t numActiveDice{0}; - -void Add(Die die) { - if(numActiveDice < maxActiveDice) { - activeDice[numActiveDice].type = die; - activeDice[numActiveDice].value = RollDie(die); - ++numActiveDice; - } -} - -void Remove(size_t at) { - if(at >= numActiveDice) { - return; - } - if(at < numActiveDice - 1) { - std::memmove(activeDice + at, activeDice + at + 1, sizeof(DieState) * (numActiveDice - at)); - } - --numActiveDice; -} - -Die Get(size_t at) { - if(at < numActiveDice) { - return activeDice[at].type; - } else return D_COIN; -} - -uint8_t GetValue(size_t at) { - if(at < numActiveDice) { - return activeDice[at].value; - } else return 0; -} - -size_t Count() { - return numActiveDice; -} - -void ReRoll() { - for(size_t i{0}; i < numActiveDice; ++i) { - activeDice[i].value = RollDie(activeDice[i].type); - } -} -} - -uint8_t RollDie(Die die) { - if(die == D_COIN) { - return std::rand() % 2; - } - return 1 + std::rand() % (die-1); -} diff --git a/src/dice_data.h b/src/dice_data.h deleted file mode 100644 index b26f6d7..0000000 --- a/src/dice_data.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include - -enum Die { - D_COIN = 2, - D4 = 4, - D6 = 6, - D8 = 8, - D10 = 10, - D12 = 12, - D20 = 20, - D100 = 100, -}; - -constexpr Die diceIndex[]{D_COIN, D4, D6, D8, D10, D12, D20, D100}; - -static inline size_t DieToIndex(Die die) { - switch(die) { - default: - case D_COIN: - return 0; - case D4: - return 1; - case D6: - return 2; - case D8: - return 3; - case D10: - return 4; - case D12: - return 5; - case D20: - return 6; - case D100: - return 7; - } - -} - -namespace active_dice { -void Add(Die die); -void Remove(size_t at); -Die Get(size_t at); -uint8_t GetValue(size_t at); -size_t Count(); -void ReRoll(); -} // namespace active_dice - -uint8_t RollDie(Die die); diff --git a/src/dice_gui.cpp b/src/dice_gui.cpp deleted file mode 100644 index da7edcb..0000000 --- a/src/dice_gui.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "dice_gui.h" -#include "SDL3/SDL_render.h" -#include "ceramic/style.h" -#include "dice_data.h" -#include "renderer/ui_data.h" -#include -#include -#include -#include - -extern SDL_Renderer *renderer; - -SDL_Texture *dieImages[8]; -SDL_Texture *hoveredDieImages[8]; -Clay_Color const dieImageColors[8]{ - {230, 184, 48, 255}, - {177, 56, 52, 255}, - {115, 177, 52, 255}, - {52, 177, 125, 255}, - {52, 177, 176, 255}, - {52, 93, 177, 255}, - {177, 52, 140, 255}, - {95, 52, 177, 255}, -}; -char const *dieImagePaths[8]{ - "assets/icons/d2.svg", - "assets/icons/d4.svg", - "assets/icons/d6.svg", - "assets/icons/d8.svg", - "assets/icons/d10.svg", - "assets/icons/d12.svg", - "assets/icons/d20.svg", - "assets/icons/d10.svg", -}; -Clay_String const dieUiStrings[8] { - CLAY_STRING("C"), - CLAY_STRING("4"), - CLAY_STRING("6"), - CLAY_STRING("8"), - CLAY_STRING("10"), - CLAY_STRING("12"), - CLAY_STRING("20"), - CLAY_STRING("100") -}; - -static void LoadDieImagesIfNeeded() { - static bool diceImagesLoaded{false}; - if(!diceImagesLoaded) { - diceImagesLoaded = true; - Clay_Color color{}; - for(size_t i{0}; i < 8; ++i) { - color = dieImageColors[i]; - dieImages[i] = cera::LoadAndStoreTexture(renderer, dieImagePaths[i]); - SDL_SetTextureColorMod(dieImages[i], color.r, color.g, color.b); - hoveredDieImages[i] = cera::LoadAndStoreTexture(renderer, dieImagePaths[i]); - color = cera::ToHoveredColor(dieImageColors[i]); - SDL_SetTextureColorMod(hoveredDieImages[i], color.r, color.g, color.b); - } - } -} - - -static void DieButton(Die die, int32_t showNumber, Clay_Sizing size, cera::OnHoveredFn onHover, intptr_t data) { - size_t const index{DieToIndex(die)}; - char resultStr[6]=""; - int32_t len = SDL_snprintf(resultStr, 6, "%d", showNumber); - Clay_String string = UiData_StoreString(resultStr, len); - CLAY_AUTO_ID({ - .layout = { - .sizing = {CLAY_SIZING_FIXED(100), CLAY_SIZING_FIXED(100)}, - .childAlignment = {CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} - }, - .image = {.imageData = Clay_Hovered() ? hoveredDieImages[index] : dieImages[index]} - }) { - Clay_OnHover(onHover, data); - cera::Header(string, 2, { - .textColor = cera::color::white - }); - } -} - -static void AddDieButtonHovered(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) { - if(pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { - active_dice::Add((Die)data); - } -} - -static void ActiveDieButtonHovered(Clay_ElementId element, Clay_PointerData pointer, intptr_t data) { - if(pointer.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) { - active_dice::Remove((size_t)data); - } -} - -void DiceContainer() { - LoadDieImagesIfNeeded(); - - Clay_Sizing const addDieButtonSize{CLAY_SIZING_FIXED(100), CLAY_SIZING_FIXED(100)}; - CLAY(CLAY_ID("DiceSelector"), cera::PanelContainer({ - .layout={ - .sizing={CLAY_SIZING_FIT(), CLAY_SIZING_GROW()}, - .childAlignment{CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} - }, - })) { - CLAY_AUTO_ID({ - .layout = { - .sizing = {CLAY_SIZING_FIT(), CLAY_SIZING_FIT()}, - .layoutDirection = CLAY_TOP_TO_BOTTOM, - }, - .clip = { - .horizontal = false, - .vertical = true, - .childOffset = Clay_GetScrollOffset(), - } - }) { - DieButton(D4, D4, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D4); - DieButton(D6, D6, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D6); - DieButton(D8, D8, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D8); - DieButton(D10, D10, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D10); - DieButton(D12, D12, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D12); - DieButton(D20, D20, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D20); - DieButton(D100, D100, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D100); - DieButton(D_COIN, D_COIN, addDieButtonSize, &AddDieButtonHovered, (intptr_t)D_COIN); - } - } - Clay_Sizing const activeDieButtonSize{CLAY_SIZING_FIXED(200), CLAY_SIZING_FIXED(200)}; - CLAY(CLAY_ID("DiceActive"), cera::PanelContainer({ - .layout={ - .sizing={CLAY_SIZING_GROW(), CLAY_SIZING_GROW()}, - .childAlignment={CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER} - } - })) { - CLAY_AUTO_ID({ - .layout = { - .sizing = {CLAY_SIZING_FIT(), CLAY_SIZING_FIT()}, - }, - .clip = { - .horizontal = true, - .vertical = false, - .childOffset = Clay_GetScrollOffset(), - }, - }) { - for(size_t i{0}; i < active_dice::Count(); ++i) { - DieButton(active_dice::Get(i), active_dice::GetValue(i), activeDieButtonSize, &ActiveDieButtonHovered, (intptr_t)i); - } - } - } -} diff --git a/src/dice_gui.h b/src/dice_gui.h deleted file mode 100644 index bc0513c..0000000 --- a/src/dice_gui.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void DiceContainer(); diff --git a/src/elements.c b/src/elements.c new file mode 100644 index 0000000..30704a4 --- /dev/null +++ b/src/elements.c @@ -0,0 +1,23 @@ +#include "elements.h" +#include "style.h" + +void TextButton(Clay_String text, Clay_Color color, OnHoveredFn onHovered, intptr_t onHoveredData) { + Clay_Color hovered = ToHoveredColor(color); + CLAY_AUTO_ID({ + .layout = { + .childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER }, + .padding = buttonPadding, + }, + .cornerRadius = buttonRadii, + .backgroundColor = Clay_Hovered() ? hovered : color, + .border = { ToHoveredColor(Clay_Hovered() ? hovered : color), CLAY_BORDER_ALL(1) } + }) { + CLAY_TEXT(text, CLAY_TEXT_CONFIG({ + .BODY(), + .textColor = TextColors(0), + .textAlignment = CLAY_TEXT_ALIGN_CENTER, + })); + Clay_OnHover(onHovered, onHoveredData); + } +} + diff --git a/src/elements.h b/src/elements.h new file mode 100644 index 0000000..f48acce --- /dev/null +++ b/src/elements.h @@ -0,0 +1,10 @@ +#ifndef ELEMENTS_H +#define ELEMENTS_H + +#include + +typedef void(*OnHoveredFn)(Clay_ElementId element, Clay_PointerData pointer, intptr_t data); + +extern void TextButton(Clay_String text, Clay_Color color, OnHoveredFn onHovered, intptr_t onHoveredData); + +#endif // !ELEMENTS_H diff --git a/src/input.cpp b/src/input.cpp deleted file mode 100644 index c04da10..0000000 --- a/src/input.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "input.h" - -namespace input { -Clay_Vector2 scrollMotion{ 0, 0 }; -bool shiftDown{ false }; -bool mouseButtonDown{ false }; - -void FrameStart() { - scrollMotion = { 0, 0 }; -} - -void HandleEvent(SDL_Event const &event) { - switch (event.type) { - case SDL_EVENT_MOUSE_WHEEL: - if (shiftDown) { - scrollMotion = (Clay_Vector2) { event.wheel.y * 4.f, -event.wheel.x * -4.f }; - } else { - scrollMotion = (Clay_Vector2) { -event.wheel.x * -4.f, event.wheel.y * 4.f }; - } - break; - case SDL_EVENT_MOUSE_MOTION: - Clay_SetPointerState((Clay_Vector2) { event.motion.x, event.motion.y }, mouseButtonDown); - break; - case SDL_EVENT_MOUSE_BUTTON_DOWN: - case SDL_EVENT_MOUSE_BUTTON_UP: - if (event.button.button == SDL_BUTTON_LEFT) { - mouseButtonDown = event.button.down; - Clay_SetPointerState((Clay_Vector2) { event.button.x, event.button.y }, mouseButtonDown); - } - break; - case SDL_EVENT_KEY_DOWN: - case SDL_EVENT_KEY_UP: - if (event.key.key == SDLK_LSHIFT || event.key.key == SDLK_RSHIFT) { - shiftDown = event.key.down; - } - break; - } -} -} diff --git a/src/input.h b/src/input.h deleted file mode 100644 index ddd2c09..0000000 --- a/src/input.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef INPUT_H -#define INPUT_H - -#include -#include - -namespace input { -extern Clay_Vector2 scrollMotion; -extern bool shiftDown; -extern bool mouseButtonDown; -void FrameStart(); -void HandleEvent(SDL_Event const &event); -} - -#endif // !INPUT_H diff --git a/src/main.cpp b/src/main.c similarity index 56% rename from src/main.cpp rename to src/main.c index 2a1c4fb..755a89d 100644 --- a/src/main.cpp +++ b/src/main.c @@ -1,25 +1,28 @@ -#define SDL_MAIN_HANDLED +#include +#include +#include +#include +#include "renderer/clay_renderer_SDL3.h" #include "application.h" -#include "input.h" -#include "ceramic/resources.h" +#include "defs.h" +#include "style.h" +#include "resources.h" + +#define SDL_MAIN_HANDLED #include #include #include -#include #include -#include #include #include #include #include #include -#include -#include -#include -#include + #include #include +#include #include constexpr SDL_InitFlags sdlInitFlags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY; @@ -33,26 +36,11 @@ uint64_t clayMemorySize = 0; Clay_Arena clayPrimaryArena; Clay_SDL3RendererData backendData = { - nullptr, nullptr, nullptr + .renderer = nullptr, + .fonts = nullptr, + .textEngine = nullptr }; -static -Clay_Dimensions MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) { - TTF_Font **fonts = (TTF_Font**)userData; - TTF_Font *font = fonts[config->fontId]; - int width, height; - TTF_SetFontSize(font, config->fontSize); - if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "MeasureText failed to measure text %s", SDL_GetError()); - } - return (Clay_Dimensions) { (float)width, (float)height }; -} - -static -void HandleClayErrors(Clay_ErrorData data) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", data.errorText.chars); -} - static inline void LogOutputResolution() { int w, h; @@ -60,6 +48,23 @@ void LogOutputResolution() { SDL_Log("output size: %i, %d", w, h); } +static +Clay_Dimensions MeasureText(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) { + TTF_Font **fonts = userData; + TTF_Font *font = fonts[config->fontId]; + int width, height; + TTF_SetFontSize(font, config->fontSize); + if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "MeasureText failed to measure text %s", SDL_GetError()); + } + return (Clay_Dimensions) { width, height }; +} + +static +void HandleClayErrors(Clay_ErrorData data) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", data.errorText.chars); +} + static inline void InitSDL() { SDL_SetHint(SDL_HINT_RENDER_LINE_METHOD, "3"); @@ -67,11 +72,11 @@ void InitSDL() { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_Init failed: %s", SDL_GetError()); exit(1); } - if (!(window = SDL_CreateWindow("Window", screenWidth, screenHeight, sdlInitFlags))) { + if ((window = SDL_CreateWindow("Window", screenWidth, screenHeight, sdlInitFlags)) == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_CreateWindow failed: %s", SDL_GetError()); exit(2); } - if (!(renderer = SDL_CreateRenderer(window, NULL))) { + if ((renderer = SDL_CreateRenderer(window, NULL)) == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_CreateRenderer failed: %s", SDL_GetError()); exit(3); } @@ -79,7 +84,7 @@ void InitSDL() { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_Init failed: %s", SDL_GetError()); exit(4); } - if (!(cera::textEngine = TTF_CreateRendererTextEngine(renderer))) { + if ((textEngine = TTF_CreateRendererTextEngine(renderer)) == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_CreateRendererTextEngine failed: %s", SDL_GetError()); exit(5); } @@ -89,55 +94,83 @@ static void InitClay() { clayMemorySize = Clay_MinMemorySize(); clayPrimaryArena = Clay_CreateArenaWithCapacityAndMemory(clayMemorySize, SDL_malloc(clayMemorySize)); - Clay_Initialize(clayPrimaryArena, { (float)screenWidth, (float)screenHeight }, { HandleClayErrors }); - Clay_SetMeasureTextFunction(MeasureText, cera::defaultFont); - Clay_SetLayoutDimensions({ (float)screenWidth, (float)screenHeight }); - float x{ 0 }, y{ 0 }; + Clay_Initialize(clayPrimaryArena, (Clay_Dimensions) { screenWidth, screenHeight }, (Clay_ErrorHandler) { HandleClayErrors }); + Clay_SetMeasureTextFunction(MeasureText, fonts); + Clay_SetLayoutDimensions((Clay_Dimensions) { screenWidth, screenHeight }); + float x, y; SDL_GetMouseState(&x, &y); Clay_SetPointerState((Clay_Vector2) { x, y }, false); } +extern Clay_RenderCommandArray RenderApplication(); +extern void HandleEvent(SDL_Event event); + int main(int argc, char *argv[]) { InitSDL(); - cera::SetDefaultFont("assets/AdwaitaSans-Regular.ttf"); + LoadResources(); LogOutputResolution(); InitClay(); - backendData = { renderer, cera::textEngine, cera::defaultFont }; + backendData = (Clay_SDL3RendererData) { + .renderer = renderer, + .fonts = fonts, + .textEngine = textEngine, + }; SDL_Event event; uint64_t startFrameTime = SDL_GetTicksNS(); double deltaTime = 0.0; + bool mouseButtonDown = false; + bool shiftDown = false; while (running) { - std::srand(SDL_GetTicksNS()); deltaTime = SDL_GetTicksNS() - startFrameTime; startFrameTime = SDL_GetTicksNS(); - UiData_Clear(); - input::FrameStart(); + Clay_Vector2 scrollMotion = { 0, 0 }; while (SDL_PollEvent(&event)) { - application::HandleEvent(event); - input::HandleEvent(event); + HandleEvent(event); switch (event.type) { case SDL_EVENT_QUIT: running = false; break; case SDL_EVENT_WINDOW_RESIZED: - Clay_SetLayoutDimensions({ - (float)event.window.data1, - (float)event.window.data2 + Clay_SetLayoutDimensions((Clay_Dimensions){ + event.window.data1, + event.window.data2 }); LogOutputResolution(); break; + case SDL_EVENT_MOUSE_WHEEL: + if (shiftDown) { + scrollMotion = (Clay_Vector2) { event.wheel.y * 2.f, -event.wheel.x * 5.f }; + } else { + scrollMotion = (Clay_Vector2) { -event.wheel.x * 2.f, event.wheel.y * 5.f }; + } + break; + case SDL_EVENT_MOUSE_MOTION: + Clay_SetPointerState((Clay_Vector2) { event.motion.x, event.motion.y }, mouseButtonDown); + break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + if (event.button.button == SDL_BUTTON_LEFT) { + mouseButtonDown = event.button.down; + Clay_SetPointerState((Clay_Vector2) { event.button.x, event.button.y }, mouseButtonDown); + } + break; + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + if (event.key.key == SDLK_LSHIFT || event.key.key == SDLK_RSHIFT) { + shiftDown = event.key.down; + } + break; default: break; } } - Clay_UpdateScrollContainers(true, input::scrollMotion, deltaTime); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + Clay_UpdateScrollContainers(true, scrollMotion, deltaTime); + SDL_SetRenderDrawColor(renderer, 10, 10, 10, 255); SDL_RenderClear(renderer); - Clay_RenderCommandArray commands{ application::RenderApplication() }; - SDL_Clay_RenderClayCommands(&backendData, &commands); + Clay_RenderCommandArray array = RenderApplication(); + SDL_Clay_RenderClayCommands(&backendData, &array); SDL_RenderPresent(renderer); SDL_Delay(10); } - cera::CleanupResources(); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/vendor/renderer/clay_renderer_SDL3.c b/src/renderer/clay_renderer_SDL3.c similarity index 97% rename from vendor/renderer/clay_renderer_SDL3.c rename to src/renderer/clay_renderer_SDL3.c index 45c67a2..0353608 100644 --- a/vendor/renderer/clay_renderer_SDL3.c +++ b/src/renderer/clay_renderer_SDL3.c @@ -20,7 +20,7 @@ static void SDL_Clay_GenerateRoundedRectCorner(SDL_Vertex *vertices, size_t begi if (count == 1) { vertices[begin] = (SDL_Vertex){ { origin.x, origin.y }, color, texel }; } else { - const double quarterCircleRadians = SDL_PI_D / 2.0; + constexpr double quarterCircleRadians = SDL_PI_D / 2.0; for (size_t i = 0; i < count; ++i) { double const angle = (quarterCircleRadians / ((double)count-1)) * i; vertices[begin + i] = (SDL_Vertex) { .position = { @@ -35,10 +35,10 @@ static void SDL_Clay_GenerateRoundedRectCorner(SDL_Vertex *vertices, size_t begi //all rendering is performed by a single SDL call, avoiding multiple RenderRect + plumbing choice for circles. static void SDL_Clay_RenderFillRoundedRect(Clay_SDL3RendererData *rendererData, const SDL_FRect rect, const Clay_CornerRadius cornerRadius, const Clay_Color clayColor) { - static const SDL_FPoint topLeftTexel = { 0, 0 }; - static const SDL_FPoint topRightTexel = { 1, 0 }; - static const SDL_FPoint bottomRightTexel = { 1, 1 }; - static const SDL_FPoint bottomLeftTexel = { 0, 1 }; + static constexpr SDL_FPoint topLeftTexel = { 0, 0 }; + static constexpr SDL_FPoint topRightTexel = { 1, 0 }; + static constexpr SDL_FPoint bottomRightTexel = { 1, 1 }; + static constexpr SDL_FPoint bottomLeftTexel = { 0, 1 }; SDL_FColor const color = { clayColor.r/255, clayColor.g/255, clayColor.b/255, clayColor.a/255 }; const float maxRadius = SDL_min(rect.w, rect.h) / 2.0f; diff --git a/vendor/renderer/clay_renderer_SDL3.h b/src/renderer/clay_renderer_SDL3.h similarity index 86% rename from vendor/renderer/clay_renderer_SDL3.h rename to src/renderer/clay_renderer_SDL3.h index 71f05da..f75558d 100644 --- a/vendor/renderer/clay_renderer_SDL3.h +++ b/src/renderer/clay_renderer_SDL3.h @@ -1,9 +1,6 @@ #ifndef CLAY_RENDERER_SDL3_H #define CLAY_RENDERER_SDL3_H -#ifdef __cplusplus -extern "C" { -#endif #include #include #include @@ -15,8 +12,5 @@ typedef struct { } Clay_SDL3RendererData; extern void SDL_Clay_RenderClayCommands(Clay_SDL3RendererData *rendererData, Clay_RenderCommandArray *rcommands); -#ifdef __cplusplus -} -#endif #endif // !CLAY_RENDERER_SDL3_H diff --git a/src/resources.c b/src/resources.c new file mode 100644 index 0000000..be4889d --- /dev/null +++ b/src/resources.c @@ -0,0 +1,98 @@ +#include "resources.h" +#include "defs.h" +#include "style.h" +#include +#include +#include + +TTF_Font *fonts[FONT_MAX]; +SDL_Texture *diceImages[DICE_IMAGE_MAX]; +SDL_Texture *diceImagesSelected[DICE_IMAGE_MAX]; +TTF_TextEngine *textEngine = nullptr; + +static inline +void LoadFonts() { + fonts[FONT_DEFAULT] = TTF_OpenFont("assets/AdwaitaSans-Regular.ttf", baseFontSize * 5); + if (fonts[FONT_DEFAULT] == nullptr) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_OpenFont failed: Failed to load adwaita sans: %s", SDL_GetError()); + exit(6); + } + TTF_SetFontHinting(fonts[FONT_DEFAULT], TTF_HINTING_LIGHT_SUBPIXEL); + fonts[FONT_BOLD] = TTF_OpenFont("assets/AdwaitaSans-Regular.ttf", baseFontSize * 5); + if (fonts[FONT_BOLD] == nullptr) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "TTF_OpenFont failed: Failed to load adwaita sans bold: %s", SDL_GetError()); + exit(6); + } + TTF_SetFontHinting(fonts[FONT_BOLD], TTF_HINTING_LIGHT_SUBPIXEL); + TTF_SetFontStyle(fonts[FONT_BOLD], TTF_STYLE_BOLD); +} + +static inline +void LoadDiceImages() { + if(!(diceImages[COIN_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d2.svg"))) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError()); + exit(7); + } + diceImagesSelected[COIN_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d2.svg"); + if(!(diceImages[D4_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d4.svg"))) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError()); + exit(7); + } + diceImagesSelected[D4_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d4.svg"); + if(!(diceImages[D6_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d6.svg"))) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError()); + exit(7); + } + diceImagesSelected[D6_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d6.svg"); + if(!(diceImages[D8_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d8.svg"))) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError()); + exit(7); + } + diceImagesSelected[D8_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d8.svg"); + if(!(diceImages[D10_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d10.svg"))) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError()); + exit(7); + } + diceImagesSelected[D10_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d10.svg"); + if(!(diceImages[D12_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d12.svg"))) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError()); + exit(7); + } + diceImagesSelected[D12_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d12.svg"); + if(!(diceImages[D20_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d20.svg"))) { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "IMG_LoadTexture Failed to load die texture: %s", SDL_GetError()); + exit(7); + } + diceImagesSelected[D20_IMAGE] = IMG_LoadTexture(renderer, "assets/icons/d20.svg"); + for (size_t i = 0; i < DICE_IMAGE_MAX; ++i) { + Clay_Color color = dieColors[i]; + SDL_SetTextureColorMod(diceImages[i], color.r, color.g, color.b); + color = ToHoveredColor(dieColors[i]); + SDL_SetTextureColorMod(diceImagesSelected[i], color.r, color.g, color.b); + } +} + +void LoadResources() { + LoadFonts(); + LoadDiceImages(); +} + +SDL_Texture *GetDiceImage(enum Dice_Die die, bool selected) { + switch (die) { + case COIN: + return selected ? diceImagesSelected[COIN_IMAGE] : diceImages[COIN_IMAGE]; + case D4: + return selected ? diceImagesSelected[D4_IMAGE] : diceImages[D4_IMAGE]; + case D6: + return selected ? diceImagesSelected[D6_IMAGE] : diceImages[D6_IMAGE]; + case D8: + return selected ? diceImagesSelected[D8_IMAGE] : diceImages[D8_IMAGE]; + case D10: + case D100: + return selected ? diceImagesSelected[D10_IMAGE] : diceImages[D10_IMAGE]; + case D12: + return selected ? diceImagesSelected[D12_IMAGE] : diceImages[D12_IMAGE]; + case D20: + return selected ? diceImagesSelected[D20_IMAGE] : diceImages[D20_IMAGE]; + } +} diff --git a/src/resources.h b/src/resources.h new file mode 100644 index 0000000..ce89368 --- /dev/null +++ b/src/resources.h @@ -0,0 +1,35 @@ +#ifndef RESOURCES_H +#define RESOURCES_H + +#include "dice.h" +#include +#include + +enum Font { + FONT_DEFAULT = 0, + FONT_BOLD = 1, + FONT_MAX +}; + +enum DiceImages { + COIN_IMAGE = 0, + D4_IMAGE = 1, + D6_IMAGE, + D8_IMAGE, + D10_IMAGE, + D12_IMAGE, + D20_IMAGE, + + DICE_IMAGE_MAX +}; + +extern TTF_TextEngine *textEngine; +extern TTF_Font *fonts[FONT_MAX]; +extern SDL_Texture *diceImages[DICE_IMAGE_MAX]; +extern SDL_Texture *diceImagesSelected[DICE_IMAGE_MAX]; + +extern void LoadResources(); + +extern SDL_Texture *GetDiceImage(enum Dice_Die die, bool selected); + +#endif // !RESOURCES_H diff --git a/src/style.c b/src/style.c new file mode 100644 index 0000000..8283649 --- /dev/null +++ b/src/style.c @@ -0,0 +1,77 @@ +#include "style.h" +#include "defs.h" +#include "dice.h" + +Clay_Color PanelBackground(size_t idx) { + return (Clay_Color) { + 255*panelBackground[idx], + 255*panelBackground[idx], + 255*panelBackground[idx], + 255 + }; +} + +Clay_Color PanelBorder(size_t idx) { + return (Clay_Color) { + 255*panelBorder[idx], + 255*panelBorder[idx], + 255*panelBorder[idx], + 255 + }; +} + +Clay_Color TextColors(size_t idx) { + return (Clay_Color) { + 255*textColorsP[idx], + 255*textColorsP[idx], + 255*textColorsP[idx], + 255 + }; +} + +Clay_Color WindowBackground() { + return (Clay_Color) { + 255*windowBackground, + 255*windowBackground, + 255*windowBackground, + 255 + }; +} + +Clay_ElementDeclaration WindowStyle() { + return (Clay_ElementDeclaration) { + .layout = { + .layoutDirection = CLAY_TOP_TO_BOTTOM, + .sizing = layoutExpand, + .padding = CLAY_PADDING_ALL(windowPadding), + .childGap = containerGap, + }, + .backgroundColor = WindowBackground() + }; +} + +Clay_Color DieColor(enum Dice_Die die) { + switch(die) { + case COIN: return dieColors[0]; + case D4: return dieColors[1]; + case D6: return dieColors[2]; + case D8: return dieColors[3]; + case D10: return dieColors[4]; + case D12: return dieColors[5]; + case D20: return dieColors[6]; + case D100: return dieColors[7]; + default: return (Clay_Color) { 0, 0, 0, 255 }; + } +} + +Clay_Color DieButtonColor(enum Dice_Die die, bool selected) { + return selected ? ToHoveredColor(DieColor(die)) : DieColor(die); +} + +Clay_Color ToHoveredColor(Clay_Color color) { + float avg = (color.r + color.g + color.b) / 3.f; + color.r = (color.r - avg) * 0.8f + avg - 30; + color.g = (color.g - avg) * 0.8f + avg - 30; + color.b = (color.b - avg) * 0.8f + avg - 30; + return color; +} diff --git a/src/style.h b/src/style.h new file mode 100644 index 0000000..9199112 --- /dev/null +++ b/src/style.h @@ -0,0 +1,126 @@ +#ifndef STYLE_H +#define STYLE_H + +#include "defs.h" +#include "resources.h" // needed for macros +#include +#include + +//////////////////////////////////// +// WINDOW STYLE +//////////////////////////////////// + +constexpr uint16_t windowPadding = 10; +constexpr float windowBackground = 0.15f; + +//////////////////////////////////// +// CONTAINER STYLE +//////////////////////////////////// + +constexpr uint16_t containerGap = 10; +constexpr double defaultRadius = 5.0; + +constexpr float panelBackground[] = { + .2f, + .3f, + .4f +}; +constexpr float panelBorder[] = { + .3f, + .4f, + .5f +}; + +constexpr Clay_Padding panelPadding = { + 24, 24, + 24, 24, +}; + +#define LISTCONTAINER(depth_)\ +border = { PanelBorder(depth_), CLAY_BORDER_ALL(2) },\ +.cornerRadius = defaultRadiusAll + +#define PANEL(depth_)\ +backgroundColor = PanelBackground(depth_),\ +.border = { PanelBorder(depth_), CLAY_BORDER_OUTSIDE(2) },\ +.cornerRadius = defaultRadiusAll + +//////////////////////////////////// +// TEXT STYLE +//////////////////////////////////// + +constexpr float paragraphGap = 10; +constexpr uint16_t baseFontSize = 16; +constexpr float textColorsP[] = { + 0.9f, + 0.9f, + 0.9f +}; + +constexpr uint16_t headerSizes[] = { + 64, 32, + 28, 16 +}; + +#define BODY()\ +fontId = FONT_DEFAULT,\ +.fontSize = baseFontSize + +#define H(level_)\ +fontId = FONT_BOLD,\ +.fontSize = headerSizes[(level_)-1] + +//////////////////////////////////// +// BUTTONS +//////////////////////////////////// + +constexpr Clay_Color warningButton = { + 177, 56, 52, 255 +}; +constexpr Clay_Color proceedButton = { + 49, 181, 99, 255 +}; +constexpr Clay_Padding buttonPadding = { + 24, 24, + 4, 4, +}; +constexpr Clay_CornerRadius buttonRadii = { + 3, 3, 3, 3 +}; + +constexpr Clay_Color dieColors[] = { + { 230, 184, 48, 255 }, + { 177, 56, 52, 255 }, + { 115, 177, 52, 255 }, + { 52, 177, 125, 255 }, + { 52, 177, 176, 255 }, + { 52, 93, 177, 255 }, + { 177, 52, 140, 255 }, + { 95, 52, 177, 255 }, +}; + +//////////////////////////////////// +// COMPILATIONS +// | Functions and expressions that combine styling data from the settings above. +//////////////////////////////////// + +constexpr Clay_Sizing layoutExpand = { + .width = CLAY_SIZING_GROW(0), + .height = CLAY_SIZING_GROW(0) +}; + +constexpr Clay_CornerRadius defaultRadiusAll = { + defaultRadius, defaultRadius, + defaultRadius, defaultRadius +}; + +extern Clay_Color PanelBackground(size_t idx); +extern Clay_Color PanelBorder(size_t idx); +extern Clay_Color TextColors(size_t idx); +extern Clay_Color WindowBackground(); +extern Clay_ElementDeclaration WindowStyle(); +extern Clay_Color DieColor(enum Dice_Die die); +extern Clay_Color DieButtonColor(enum Dice_Die die, bool selected); +extern Clay_Color ToHoveredColor(Clay_Color color); + +#endif // !STYLE_H diff --git a/vendor/SDL3 b/vendor/SDL3 deleted file mode 160000 index be1d442..0000000 --- a/vendor/SDL3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit be1d44279c715c5ac879f5698bc604fe9070ff0c diff --git a/vendor/SDL3_image b/vendor/SDL3_image deleted file mode 160000 index 3bbb15f..0000000 --- a/vendor/SDL3_image +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3bbb15f654d48251c164e4dc4f5ae8b9ee0eae71 diff --git a/vendor/SDL3_ttf b/vendor/SDL3_ttf deleted file mode 160000 index 215872a..0000000 --- a/vendor/SDL3_ttf +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 215872a0f91d24a4013f106832c32d879fba48db diff --git a/vendor/ceramic b/vendor/ceramic deleted file mode 160000 index 2dcdcd3..0000000 --- a/vendor/ceramic +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2dcdcd394c5b0c7e4875f965dfe789d3c71ad217 diff --git a/vendor/renderer/ui_data.c b/vendor/renderer/ui_data.c deleted file mode 100644 index 9a78fc3..0000000 --- a/vendor/renderer/ui_data.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "ui_data.h" -#include -#include - -const size_t uiDataLength = UI_DATA_SIZE; - -static size_t utilized = 0; -static char uiDataArena[UI_DATA_SIZE]; -static char *uiDataWriter = uiDataArena; - -Clay_String UiData_StoreString(char const *data, size_t amount) { - if (utilized + amount > uiDataLength) { - return CLAY_STRING("out of arena memory"); - } - memcpy(uiDataWriter, data, amount); - Clay_String result = { - false, (int32_t)amount, uiDataWriter - }; - uiDataWriter += amount; - utilized += amount; - return result; -} -Clay_String UiData_StoreClayStr(Clay_String string) { - return UiData_StoreString(string.chars, string.length); -} - -void UiData_Clear() { - utilized = 0; - uiDataWriter = uiDataArena; -} diff --git a/vendor/renderer/ui_data.h b/vendor/renderer/ui_data.h deleted file mode 100644 index ac62421..0000000 --- a/vendor/renderer/ui_data.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef UI_DATA_H -#define UI_DATA_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef UI_DATA_SIZE -#define UI_DATA_SIZE 1024*1024 -#endif - -extern Clay_String UiData_StoreString(char const *data, size_t amount); -extern Clay_String UiData_StoreClayStr(Clay_String str); -extern void UiData_Clear(); - -#ifdef __cplusplus -} -#endif - -#endif // !UI_DATA_