Compare commits

...

3 Commits

Author SHA1 Message Date
Eigen Lenk
2262a20754
Merge e5cf195d8a into 4ee501019c 2025-02-26 03:44:25 +01:00
Joram Vandemoortele
4ee501019c
[Compilers] Added DLL macro to support .dll building (#278)
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled
2025-02-26 15:37:51 +13:00
Eigen Lenk
e5cf195d8a [Renderers/Allegro4] Add Allegro 4.x renderer and demo 2025-02-20 11:47:25 +02:00
11 changed files with 865 additions and 35 deletions

View File

@ -474,6 +474,7 @@ Clay supports C preprocessor directives to modulate functionality at compile tim
The supported directives are:
- `CLAY_WASM` - Required when targeting Web Assembly.
- `CLAY_DLL` - Required when creating a .Dll file.
### Bindings for non C

76
clay.h
View File

@ -42,6 +42,12 @@
#define CLAY_WASM_EXPORT(null)
#endif
#ifdef CLAY_DLL
#define CLAY_DLL_EXPORT __declspec(dllexport) __stdcall
#else
#define CLAY_DLL_EXPORT
#endif
// Public Macro API ------------------------
#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y))
@ -769,102 +775,102 @@ typedef struct {
// Public API functions ------------------------------------------
// Returns the size, in bytes, of the minimum amount of memory Clay requires to operate at its current settings.
uint32_t Clay_MinMemorySize(void);
CLAY_DLL_EXPORT uint32_t Clay_MinMemorySize(void);
// Creates an arena for clay to use for its internal allocations, given a certain capacity in bytes and a pointer to an allocation of at least that size.
// Intended to be used with Clay_MinMemorySize in the following way:
// uint32_t minMemoryRequired = Clay_MinMemorySize();
// Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(minMemoryRequired, malloc(minMemoryRequired));
Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *memory);
CLAY_DLL_EXPORT Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *memory);
// Sets the state of the "pointer" (i.e. the mouse or touch) in Clay's internal data. Used for detecting and responding to mouse events in the debug view,
// as well as for Clay_Hovered() and scroll element handling.
void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown);
CLAY_DLL_EXPORT void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown);
// Initialize Clay's internal arena and setup required data before layout can begin. Only needs to be called once.
// - arena can be created using Clay_CreateArenaWithCapacityAndMemory()
// - layoutDimensions are the initial bounding dimensions of the layout (i.e. the screen width and height for a full screen layout)
// - errorHandler is used by Clay to inform you if something has gone wrong in configuration or layout.
Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler);
CLAY_DLL_EXPORT Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler);
// Returns the Context that clay is currently using. Used when using multiple instances of clay simultaneously.
Clay_Context* Clay_GetCurrentContext(void);
CLAY_DLL_EXPORT Clay_Context* Clay_GetCurrentContext(void);
// Sets the context that clay will use to compute the layout.
// Used to restore a context saved from Clay_GetCurrentContext when using multiple instances of clay simultaneously.
void Clay_SetCurrentContext(Clay_Context* context);
CLAY_DLL_EXPORT void Clay_SetCurrentContext(Clay_Context* context);
// Updates the state of Clay's internal scroll data, updating scroll content positions if scrollDelta is non zero, and progressing momentum scrolling.
// - enableDragScrolling when set to true will enable mobile device like "touch drag" scroll of scroll containers, including momentum scrolling after the touch has ended.
// - scrollDelta is the amount to scroll this frame on each axis in pixels.
// - deltaTime is the time in seconds since the last "frame" (scroll update)
void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime);
CLAY_DLL_EXPORT void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime);
// Updates the layout dimensions in response to the window or outer container being resized.
void Clay_SetLayoutDimensions(Clay_Dimensions dimensions);
CLAY_DLL_EXPORT void Clay_SetLayoutDimensions(Clay_Dimensions dimensions);
// Called before starting any layout declarations.
void Clay_BeginLayout(void);
CLAY_DLL_EXPORT void Clay_BeginLayout(void);
// Called when all layout declarations are finished.
// Computes the layout and generates and returns the array of render commands to draw.
Clay_RenderCommandArray Clay_EndLayout(void);
CLAY_DLL_EXPORT Clay_RenderCommandArray Clay_EndLayout(void);
// Calculates a hash ID from the given idString.
// Generally only used for dynamic strings when CLAY_ID("stringLiteral") can't be used.
Clay_ElementId Clay_GetElementId(Clay_String idString);
CLAY_DLL_EXPORT Clay_ElementId Clay_GetElementId(Clay_String idString);
// Calculates a hash ID from the given idString and index.
// - index is used to avoid constructing dynamic ID strings in loops.
// Generally only used for dynamic strings when CLAY_IDI("stringLiteral", index) can't be used.
Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index);
CLAY_DLL_EXPORT Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index);
// Returns layout data such as the final calculated bounding box for an element with a given ID.
// The returned Clay_ElementData contains a `found` bool that will be true if an element with the provided ID was found.
// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
Clay_ElementData Clay_GetElementData(Clay_ElementId id);
CLAY_DLL_EXPORT Clay_ElementData Clay_GetElementData(Clay_ElementId id);
// Returns true if the pointer position provided by Clay_SetPointerState is within the current element's bounding box.
// Works during element declaration, e.g. CLAY({ .backgroundColor = Clay_Hovered() ? BLUE : RED });
bool Clay_Hovered(void);
CLAY_DLL_EXPORT bool Clay_Hovered(void);
// Bind a callback that will be called when the pointer position provided by Clay_SetPointerState is within the current element's bounding box.
// - onHoverFunction is a function pointer to a user defined function.
// - userData is a pointer that will be transparently passed through when the onHoverFunction is called.
void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, intptr_t userData), intptr_t userData);
CLAY_DLL_EXPORT void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, intptr_t userData), intptr_t userData);
// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.
// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
bool Clay_PointerOver(Clay_ElementId elementId);
CLAY_DLL_EXPORT bool Clay_PointerOver(Clay_ElementId elementId);
// Returns data representing the state of the scrolling element with the provided ID.
// The returned Clay_ScrollContainerData contains a `found` bool that will be true if a scroll element was found with the provided ID.
// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.
// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id);
CLAY_DLL_EXPORT Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id);
// Binds a callback function that Clay will call to determine the dimensions of a given string slice.
// - measureTextFunction is a user provided function that adheres to the interface Clay_Dimensions (Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);
// - userData is a pointer that will be transparently passed through when the measureTextFunction is called.
void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData), void *userData);
CLAY_DLL_EXPORT void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData), void *userData);
// Experimental - Used in cases where Clay needs to integrate with a system that manages its own scrolling containers externally.
// Please reach out if you plan to use this function, as it may be subject to change.
void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId, void *userData), void *userData);
CLAY_DLL_EXPORT void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId, void *userData), void *userData);
// A bounds-checked "get" function for the Clay_RenderCommandArray returned from Clay_EndLayout().
Clay_RenderCommand * Clay_RenderCommandArray_Get(Clay_RenderCommandArray* array, int32_t index);
CLAY_DLL_EXPORT Clay_RenderCommand * Clay_RenderCommandArray_Get(Clay_RenderCommandArray* array, int32_t index);
// Enables and disables Clay's internal debug tools.
// This state is retained and does not need to be set each frame.
void Clay_SetDebugModeEnabled(bool enabled);
CLAY_DLL_EXPORT void Clay_SetDebugModeEnabled(bool enabled);
// Returns true if Clay's internal debug tools are currently enabled.
bool Clay_IsDebugModeEnabled(void);
CLAY_DLL_EXPORT bool Clay_IsDebugModeEnabled(void);
// Enables and disables visibility culling. By default, Clay will not generate render commands for elements whose bounding box is entirely outside the screen.
void Clay_SetCullingEnabled(bool enabled);
CLAY_DLL_EXPORT void Clay_SetCullingEnabled(bool enabled);
// Returns the maximum number of UI elements supported by Clay's current configuration.
int32_t Clay_GetMaxElementCount(void);
CLAY_DLL_EXPORT int32_t Clay_GetMaxElementCount(void);
// Modifies the maximum number of UI elements supported by Clay's current configuration.
// This may require reallocating additional memory, and re-calling Clay_Initialize();
void Clay_SetMaxElementCount(int32_t maxElementCount);
CLAY_DLL_EXPORT void Clay_SetMaxElementCount(int32_t maxElementCount);
// Returns the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
int32_t Clay_GetMaxMeasureTextCacheWordCount(void);
CLAY_DLL_EXPORT int32_t Clay_GetMaxMeasureTextCacheWordCount(void);
// Modifies the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
// This may require reallocating additional memory, and re-calling Clay_Initialize();
void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);
CLAY_DLL_EXPORT void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);
// Resets Clay's internal text measurement cache, useful if memory to represent strings is being re-used.
// Similar behaviour can be achieved on an individual text element level by using Clay_TextElementConfig.hashStringContents
void Clay_ResetMeasureTextCache(void);
CLAY_DLL_EXPORT void Clay_ResetMeasureTextCache(void);
// Internal API functions required by macros ----------------------
void Clay__OpenElement(void);
void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);
void Clay__CloseElement(void);
Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed);
void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config);
uint32_t Clay__GetParentElementId(void);
CLAY_DLL_EXPORT void Clay__OpenElement(void);
CLAY_DLL_EXPORT void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);
CLAY_DLL_EXPORT void Clay__CloseElement(void);
CLAY_DLL_EXPORT Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed);
CLAY_DLL_EXPORT void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
CLAY_DLL_EXPORT Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config);
CLAY_DLL_EXPORT uint32_t Clay__GetParentElementId(void);
extern Clay_Color Clay__debugViewHighlightColor;
extern uint32_t Clay__debugViewWidth;

View File

@ -0,0 +1,3 @@
# Allegro 4 demo
To build this demo on your system of choice, include all source files in this directory, as well as in "renderers/alleg4" and link with a 4.x version of Allegro. It's probably easiest to go with a pre-built binary.

View File

@ -0,0 +1,486 @@
#include "renderers/alleg4/alleg4.h"
#include <stdio.h>
#include <math.h>
// Locked 50 FPS
#define TICKRATE_MS 20
#define DEG_TO_RAD(x) ((x) / 180.0 * M_PI)
static volatile int pending_main_loop_update = 0;
void main_loop_ticker() {
pending_main_loop_update = 1;
}
END_OF_FUNCTION(main_loop_ticker);
static const int FONT_ID_BODY_16 = 0;
static const int FONT_ID_LARGE_TITLE = 1;
static Clay_Color COLOR_BLACK = { 0, 0, 0, 255 };
static Clay_Color COLOR_WHITE = { 255, 255, 255, 255 };
static Clay_Color COLOR_RED = { 255, 0, 0, 255 };
#define EXPANDING_LAYOUT { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }
#define BORDER_DEPTH { .width = { 1, 2, 1, 2, 0 }, .color = COLOR_BLACK }
#define BEVELED(PADDING, BG, WIDTH, ...) \
CLAY({ \
.backgroundColor = BG, \
.border = { .width = { 1, 0, 1, 0, 0 }, .color = COLOR_WHITE }, \
.layout.sizing.height = CLAY_SIZING_GROW(0), \
.layout.sizing.width = WIDTH > 0 ? CLAY_SIZING_FIXED(WIDTH) : WIDTH == 0 ? CLAY_SIZING_GROW() : CLAY_SIZING_FIT() \
}) { \
CLAY({ \
.layout.padding.left = PADDING, \
.layout.padding.right = PADDING, \
.layout.sizing.width = CLAY_SIZING_GROW(), \
.layout.sizing.height = CLAY_SIZING_GROW(), \
.layout.childAlignment.x = CLAY_ALIGN_X_CENTER, \
.layout.childAlignment.y = CLAY_ALIGN_Y_CENTER, \
.border = { .width = { 0, 1, 0, 1, 0 }, .color = { 128, 128, 128, 255 }, } \
}) { \
__VA_ARGS__ \
} \
}
static struct {
BITMAP *lib_logo;
BITMAP *environment_category;
BITMAP *coral_reef;
} R;
static void draw_bouncy_ball(BITMAP *buffer, Clay_BoundingBox box, void *user_data) {
#define BALL_R 4
#define BORDER 1
static int x = BALL_R+BORDER, y = BALL_R+BORDER;
static Clay_Vector2 dir = { 1, 1 };
circlefill(buffer, box.x + x, box.y + y, BALL_R, ALLEGCOLOR(COLOR_RED));
x += dir.x;
y += dir.y;
if (x <= BALL_R+BORDER || x >= box.width - (BALL_R+BORDER)) {
dir.x *= -1;
}
if (y <= BALL_R+BORDER || y >= box.height - (BALL_R+BORDER)) {
dir.y *= -1;
}
}
static Clay_String test_article;
typedef struct {
intptr_t offset;
intptr_t memory;
} EncartaDemo_Arena;
typedef struct {
EncartaDemo_Arena frameArena;
unsigned int counter;
} EncartaDemo_Data;
EncartaDemo_Data EncartaDemo_Initialize() {
test_article = CLAY_STRING("A coral reef is an underwater ecosystem characterized by reef-building corals. Reefs are formed of colonies of coral polyps held together by calcium carbonate. Most coral reefs are built from stony corals, whose polyps cluster in groups.\n\nCoral belongs to the class Anthozoa in the animal phylum Cnidaria, which includes sea anemones and jellyfish. Unlike sea anemones, corals secrete hard carbonate exoskeletons that support and protect the coral. Most reefs grow best in warm, shallow, clear, sunny and agitated water. Coral reefs first appeared 485 million years ago, at the dawn of the Early Ordovician, displacing the microbial and sponge reefs of the Cambrian.\n\nSometimes called rainforests of the sea, shallow coral reefs form some of Earth's most diverse ecosystems. They occupy less than 0.1% of the world's ocean area, about half the area of France, yet they provide a home for at least 25% of all marine species, including fish, mollusks, worms, crustaceans, echinoderms, sponges, tunicates and other cnidarians. Coral reefs flourish in ocean waters that provide few nutrients. They are most commonly found at shallow depths in tropical waters, but deep water and cold water coral reefs exist on smaller scales in other areas.\n\nShallow tropical coral reefs have declined by 50% since 1950, partly because they are sensitive to water conditions. They are under threat from excess nutrients (nitrogen and phosphorus), rising ocean heat content and acidification, overfishing (e.g., from blast fishing, cyanide fishing, spearfishing on scuba), sunscreen use, and harmful land-use practices, including runoff and seeps (e.g., from injection wells and cesspools).\n\nCoral reefs deliver ecosystem services for tourism, fisheries and shoreline protection. The annual global economic value of coral reefs has been estimated at anywhere from US$30-375 billion (1997 and 2003 estimates) to US$2.7 trillion (a 2020 estimate) to US$9.9 trillion (a 2014 estimate).\n\nThough the shallow water tropical coral reefs are best known, there are also deeper water reef-forming corals, which live in colder water and in temperate seas.\n\n\n(( Material ))\n\nAs the name implies, coral reefs are made up of coral skeletons from mostly intact coral colonies. As other chemical elements present in corals become incorporated into the calcium carbonate deposits, aragonite is formed. However, shell fragments and the remains of coralline algae such as the green-segmented genus Halimeda can add to the reef's ability to withstand damage from storms and other threats. Such mixtures are visible in structures such as Eniwetok Atoll.");
EncartaDemo_Data data = {
.frameArena = { .memory = (intptr_t)malloc(1024) },
.counter = 0
};
return data;
}
void BeveledButton(Clay_String text, int padding, int fixedWidth) {
BEVELED(padding, Clay_Hovered() ? ((Clay_Color) { 224, 224, 224, 255 }) : ((Clay_Color) { 192, 192, 192, 255 }), fixedWidth, {
CLAY_TEXT(text, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
.fontSize = 16,
.textColor = COLOR_BLACK,
.textAlignment = CLAY_TEXT_ALIGN_CENTER,
.lineHeight = 12
}));
})
}
static void menubar(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("menubar"),
.backgroundColor = {64, 64, 64, 255 },
.border = {
.width = { 0, 0, 0, 1, 0 },
.color = COLOR_BLACK
},
.layout = {
.sizing = {
.height = CLAY_SIZING_FIXED(24),
.width = CLAY_SIZING_GROW(0)
},
}
}) {
BeveledButton(CLAY_STRING("Menu"), 12, -1);
BeveledButton(CLAY_STRING("Contents"), 12, -1);
BeveledButton(CLAY_STRING("Find"), 12, -1);
BeveledButton(CLAY_STRING("Go Back"), 12, -1);
BeveledButton(CLAY_STRING("Gallery"), 12, -1);
BeveledButton(CLAY_STRING("Atlas"), 12, -1);
BeveledButton(CLAY_STRING("Timeline"), 12, -1);
BeveledButton(CLAY_STRING("Help"), 12, -1);
BEVELED(0, ((Clay_Color) { 192, 192, 192, 255 }), 0, { });
}
}
static void largeArticleHeading(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("titlebar"),
.layout = {
.sizing.width = CLAY_SIZING_GROW(),
.padding = { 8, 8, 8, 0 },
}
}) {
CLAY({
.backgroundColor = {255, 255, 128, 255 },
.cornerRadius = { 12, 0, 0, 12 },
.layout = {
.sizing = {
.height = CLAY_SIZING_GROW(),
.width = CLAY_SIZING_GROW()
},
.childAlignment = { .x = CLAY_ALIGN_X_CENTER, .y = CLAY_ALIGN_Y_CENTER }
},
.border = { .width = CLAY_BORDER_ALL(1), .color = COLOR_BLACK }
}) {
CLAY_TEXT(CLAY_STRING("Coral Reef"), CLAY_TEXT_CONFIG({
.fontId = FONT_ID_LARGE_TITLE,
.textColor = COLOR_BLACK
}));
}
}
}
static void demoTitleBox(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("DemoHeader"),
.backgroundColor = COLOR_WHITE,
.layout.sizing.width = CLAY_SIZING_FIXED(250),
.layout.padding = CLAY_PADDING_ALL(5),
.layout.childGap = 5,
.border = BORDER_DEPTH,
.cornerRadius = { 8, 8, 0, 0 }
}) {
CLAY({
.id = CLAY_ID("LibraryLogo"),
.layout = { .sizing = CLAY_SIZING_FIXED(28) },
.image = { .imageData = R.lib_logo, .sourceDimensions = {28, 28} },
.backgroundColor = { 128+sin(DEG_TO_RAD(data->counter))*127, 0, 128+sin(DEG_TO_RAD(data->counter/2))*127, 128 }
}) {}
CLAY_TEXT(CLAY_STRING("Clay Encarta 95:\nAllegro4 Demo"), CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
.textColor = COLOR_BLACK
}));
}
}
static void categoryBox(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("CategoryImage"),
.backgroundColor = { 90, 90, 90, 255 },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = {
.width = CLAY_SIZING_FIXED(250),
.height = CLAY_SIZING_FIT()
}
},
.border = BORDER_DEPTH
}) {
CLAY({
.image = {
.imageData = R.environment_category,
.sourceDimensions = {250, 120}
},
.layout.sizing.height = CLAY_SIZING_FIXED(120),
.layout.padding = CLAY_PADDING_ALL(6),
.layout.childAlignment.y = CLAY_ALIGN_Y_BOTTOM
}) {
CLAY_TEXT(CLAY_STRING("Environment"), CLAY_TEXT_CONFIG({
.fontId = FONT_ID_LARGE_TITLE,
.textColor = COLOR_BLACK
}));
}
CLAY({
.backgroundColor = {64, 64, 64, 255 },
.layout = {
.padding = { 1, 2, 1, 2 },
.sizing = {
.width = CLAY_SIZING_GROW(),
.height = CLAY_SIZING_FIT(32),
}
}
}) {
BeveledButton(CLAY_STRING("Show List"), 8, 0);
BeveledButton(CLAY_STRING("Change\nCategory"), 8, 0);
BeveledButton(CLAY_STRING("<"), 8, 28);
BeveledButton(CLAY_STRING(">"), 8, 28);
}
}
}
static void articleBox(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("ArticleImage"),
.backgroundColor = { 90, 90, 90, 255 },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing.width = CLAY_SIZING_FIXED(250)
},
.border = BORDER_DEPTH
}) {
CLAY({
.image = {
.imageData = R.coral_reef,
.sourceDimensions = {250, 230}
},
.layout.sizing.height = CLAY_SIZING_FIXED(230)
}) {}
CLAY({
.layout.sizing.height = CLAY_SIZING_FIXED(20),
.layout.sizing.width = CLAY_SIZING_GROW(),
.layout.padding = { 1, 1, 1, 0 },
}) {
BEVELED(0, ((Clay_Color) { 192, 192, 192, 255 }), 0, {
CLAY_TEXT(CLAY_STRING("Biodiversity of a coral reef"), CLAY_TEXT_CONFIG({
.fontId = -1,
.textColor = COLOR_BLACK
}));
});
}
CLAY({
.backgroundColor = {64, 64, 64, 255 },
.layout = {
.padding = { 1, 2, 1, 2 },
.sizing = {
.height = CLAY_SIZING_FIT(32),
.width = CLAY_SIZING_GROW()
}
}
}) {
BeveledButton(CLAY_STRING("Copy"), 8, 0);
BeveledButton(CLAY_STRING("Print"), 8, 0);
BeveledButton(CLAY_STRING("<"), 8, 28);
BeveledButton(CLAY_STRING(">"), 8, 28);
}
}
}
static void sidebar(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("sidebar"),
.layout = {
.childGap = 8,
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing.width = CLAY_SIZING_FIT()
}
}) {
demoTitleBox(data);
categoryBox(data);
articleBox(data);
}
}
static void article(EncartaDemo_Data *data) {
CLAY({
.id = CLAY_ID("article"),
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = EXPANDING_LAYOUT
},
.backgroundColor = COLOR_WHITE,
.border = BORDER_DEPTH,
.cornerRadius = { 8, 8, 0, 0 },
}) {
CLAY({
.scroll.vertical = true,
.layout.padding = CLAY_PADDING_ALL(8),
}) {
CLAY_TEXT(test_article, CLAY_TEXT_CONFIG({
.fontId = FONT_ID_BODY_16,
.textColor = COLOR_BLACK
}));
}
CLAY({
.backgroundColor = {64, 64, 64, 255 },
.layout = {
.padding = { 1, 2, 1, 2 },
.sizing = {
.height = CLAY_SIZING_FIT(32),
.width = CLAY_SIZING_GROW()
}
}
}) {
BeveledButton(CLAY_STRING("Outline"), 8, 0);
BeveledButton(CLAY_STRING("See Also"), 8, 0);
BeveledButton(CLAY_STRING("Copy"), 8, 0);
BeveledButton(CLAY_STRING("Print"), 8, 0);
BEVELED(0, ((Clay_Color) { 192, 192, 192, 255 }), 64, {
alleg4_custom_element *ball_renderer = (alleg4_custom_element *)(data->frameArena.memory + data->frameArena.offset);
data->frameArena.offset += sizeof(alleg4_custom_element);
*ball_renderer = (alleg4_custom_element) {
&draw_bouncy_ball, NULL
};
CLAY({
.layout = { .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) } },
.custom = { .customData = ball_renderer }
}) { }
})
}
}
}
Clay_RenderCommandArray EncartaDemo_CreateLayout(EncartaDemo_Data *data) {
data->frameArena.offset = 0;
Clay_BeginLayout();
// Build UI here
CLAY({
.id = CLAY_ID("outer-container"),
.backgroundColor = { 64, 64, 64, 255 },
.layout = {
.layoutDirection = CLAY_TOP_TO_BOTTOM,
.sizing = EXPANDING_LAYOUT
}
}) {
menubar(data);
largeArticleHeading(data);
CLAY({
.id = CLAY_ID("lower-content"),
.backgroundColor = { 64, 64, 64, 255 },
.layout = {
.sizing = EXPANDING_LAYOUT,
.childGap = 8,
.padding = CLAY_PADDING_ALL(8)
}
}) {
sidebar(data);
article(data);
}
}
return Clay_EndLayout();
}
void HandleClayErrors(Clay_ErrorData errorData) {
printf("%s", errorData.errorText.chars);
}
int main(int argc, char *argv[]) {
EncartaDemo_Data demoData = EncartaDemo_Initialize();
BITMAP *buffer;
if (allegro_init() != 0) {
return 1;
}
install_keyboard();
install_mouse();
install_timer();
set_color_depth(16);
int driver = GFX_AUTODETECT;
#ifdef _WIN32
driver = GFX_AUTODETECT_WINDOWED;
#endif
set_window_title("Clay Encarta 95");
if (set_gfx_mode(driver, 800, 600, 0, 0) != 0) {
if (set_gfx_mode(GFX_SAFE, 800, 600, 0, 0) != 0) {
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
return 2;
}
}
if (install_int(main_loop_ticker, TICKRATE_MS) < 0) {
allegro_message("Error installing interrupt\n%s\n", allegro_error);
return 4;
}
LOCK_VARIABLE(pending_main_loop_update);
LOCK_FUNCTION(main_loop_ticker);
alleg4_init_fonts(2); /* Allocate space for 1 font */
alleg4_set_font(FONT_ID_BODY_16, load_font("res/8x16.pcx", NULL, NULL));
alleg4_set_font(FONT_ID_LARGE_TITLE, load_font("res/ex06.pcx", NULL, NULL));
buffer = create_bitmap(SCREEN_W, SCREEN_H);
R.lib_logo = load_bitmap("res/liblogo.pcx", NULL);
R.environment_category = load_bitmap("res/env.pcx", NULL);
R.coral_reef = load_bitmap("res/coral.pcx", NULL);
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
Clay_Initialize(clayMemory, (Clay_Dimensions) {
(float)SCREEN_W, (float)SCREEN_H
}, (Clay_ErrorHandler) {
HandleClayErrors
});
Clay_SetMeasureTextFunction(alleg4_measure_text, NULL);
show_os_cursor(MOUSE_CURSOR_ARROW);
enable_hardware_cursor();
int _mouse_w = mouse_w, _mouse_z = mouse_z;
while (!keypressed()) {
if (!pending_main_loop_update) {
continue;
}
pending_main_loop_update = false;
clear_to_color(buffer, makecol(0, 0, 0));
if (mouse_needs_poll()) {
poll_mouse();
}
Clay_SetLayoutDimensions((Clay_Dimensions) {
(float)SCREEN_W, (float)SCREEN_H
});
Clay_SetPointerState((Clay_Vector2) {
mouse_x, mouse_y
}, mouse_b & 1);
Clay_UpdateScrollContainers(true, (Clay_Vector2) {
mouse_w - _mouse_w, mouse_z - _mouse_z
}, (float)TICKRATE_MS / 1000.f);
_mouse_w = mouse_w;
_mouse_z = mouse_z;
Clay_RenderCommandArray renderCommands = EncartaDemo_CreateLayout(&demoData);
alleg4_render(buffer, renderCommands);
demoData.counter ++;
if (gfx_capabilities & GFX_HW_CURSOR) {
blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
} else {
show_mouse(NULL);
blit(buffer, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
show_mouse(screen);
}
}
return 0;
}
END_OF_MAIN()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

306
renderers/alleg4/alleg4.c Normal file
View File

@ -0,0 +1,306 @@
#define CLAY_IMPLEMENTATION
#include "alleg4.h"
#define CLIPPED_OPERATION(BUF, X, Y, W, H, CODE) { \
int _X0, _Y0, _X1, _Y1; \
get_clip_rect(BUF, &_X0, &_Y0, &_X1, &_Y1); \
set_clip_rect(BUF, X, Y, X+W-1, Y+H-1); \
CODE; \
set_clip_rect(BUF, _X0, _Y0, _X1, _Y1); \
}
typedef struct alleg4_font_store {
size_t size;
FONT *fonts[];
} alleg4_font_store;
static alleg4_font_store *font_store = NULL;
void alleg4_init_fonts(size_t size) {
font_store = malloc(sizeof(alleg4_font_store) + sizeof(FONT*[size]));
font_store->size = size;
for (size_t i = 0; i < size; ++i) {
font_store->fonts[i] = NULL;
}
}
void alleg4_set_font(unsigned int font_id, FONT *font_object) {
font_store->fonts[font_id] = font_object;
}
static inline FONT* get_font(unsigned int font_id) {
if (font_id >= 0 && font_id < font_store->size) {
return font_store->fonts[font_id];
} else {
return font; /* Default built-in font */
}
}
static inline void arc_thickness(
BITMAP *dst,
int x,
int y,
fixed from,
fixed to,
int r,
int color,
int thickness
) {
do {
arc(dst, x, y, from, to, r--, color);
} while (--thickness);
}
static inline void rectfill_wh(
BITMAP *dst,
int x,
int y,
int w,
int h,
int color
) {
// rectfill uses stard and end coordinates instead of size, so we'd have to -1 all over the place
if (w == 1) { vline(dst, x, y, y+h-1, color); }
else if (h == 1) { hline(dst, x, y, x+w-1, color); }
else { rectfill(dst, x, y, x+w-1, y+h-1, color); }
}
/* Radiuses array contains corner radiuses in clockwise order starting from top-left */
static inline void roundrectfill(
BITMAP *dst,
int x,
int y,
int w,
int h,
int color,
int r[]
) {
int top = CLAY__MAX(r[0],r[1]);
int bottom = CLAY__MAX(r[2],r[3]);
int left = CLAY__MAX(r[0],r[3]);
int right = CLAY__MAX(r[1],r[2]);
if (r[0]) {
CLIPPED_OPERATION(dst, x, y, r[0], r[0], {
circlefill(dst, x+r[0], y+r[0], r[0], color);
});
}
if (r[1]) {
CLIPPED_OPERATION(dst, x+w-r[1], y, r[1], r[1], {
circlefill(dst, x+w-1-r[1], y+r[1], r[1], color);
});
}
if (r[2]) {
CLIPPED_OPERATION(dst, x+w-r[2], y+h-r[2], r[2], r[2], {
circlefill(dst, x+w-1-r[2], y+h-1-r[2], r[2], color);
});
}
if (r[3]) {
CLIPPED_OPERATION(dst, x, y+h-r[3], r[3], r[3], {
circlefill(dst, x+r[3], y+h-1-r[3], r[3], color);
});
}
if (top) {
rectfill_wh(dst, x+r[0], y, w-r[0]-r[1], top, color);
}
if (bottom) {
rectfill_wh(dst, x+r[3], y+h-bottom, w-r[2]-r[3], bottom, color);
}
if (left) {
rectfill_wh(dst, x, y+top, left, h-top-bottom, color);
}
if (right) {
rectfill_wh(dst, x+w-right, y+top, right, h-top-bottom, color);
}
rectfill_wh(dst, x+left, y+top, w-left-right, h-top-bottom, color);
}
void alleg4_render(
BITMAP *buffer,
Clay_RenderCommandArray renderCommands
) {
static int crx0, cry0, crx1, cry1;
for (uint32_t i = 0; i < renderCommands.length; i++) {
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, i);
Clay_BoundingBox box = renderCommand->boundingBox;
switch (renderCommand->commandType) {
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
Clay_RectangleRenderData *config = &renderCommand->renderData.rectangle;
Clay_Color color = config->backgroundColor;
int radiuses[] = {
config->cornerRadius.topLeft,
config->cornerRadius.topRight,
config->cornerRadius.bottomRight,
config->cornerRadius.bottomLeft
};
if (radiuses[0] + radiuses[1] + radiuses[2] + radiuses[3] > 0) {
roundrectfill(buffer, box.x, box.y, box.width, box.height, ALLEGCOLOR(config->backgroundColor), radiuses);
} else {
rectfill_wh(buffer, box.x, box.y, box.width, box.height, ALLEGCOLOR(config->backgroundColor));
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
Clay_TextRenderData *config = &renderCommand->renderData.text;
char *slice = (char *)calloc(config->stringContents.length + 1, 1);
memcpy(slice, config->stringContents.chars, config->stringContents.length);
FONT *font_object = get_font(config->fontId);
if (is_color_font(font_object)) {
textout_ex(buffer, get_font(config->fontId), slice, box.x, box.y, -1, -1);
} else {
textout_ex(buffer, get_font(config->fontId), slice, box.x, box.y, ALLEGCOLOR(config->textColor), -1);
}
free(slice);
break;
}
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
Clay_ImageRenderData *config = &renderCommand->renderData.image;
Clay_Color tintColor = config->backgroundColor;
BITMAP *image = (BITMAP *)config->imageData;
if (tintColor.r + tintColor.g + tintColor.b == 0) {
draw_sprite(buffer, image, box.x, box.y);
} else {
set_trans_blender(tintColor.r, tintColor.g, tintColor.b, 0);
draw_lit_sprite(buffer, image, box.x, box.y, tintColor.a);
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_START: {
get_clip_rect(buffer, &crx0, &cry0, &crx1, &cry1); /* Save current clip rect coordinates */
set_clip_rect(buffer, box.x, box.y, box.x + box.width, box.y + box.height);
break;
}
case CLAY_RENDER_COMMAND_TYPE_SCISSOR_END: {
set_clip_rect(buffer, crx0, cry0, crx1, cry1);
break;
}
case CLAY_RENDER_COMMAND_TYPE_BORDER: {
Clay_BorderRenderData *config = &renderCommand->renderData.border;
const float tl = config->cornerRadius.topLeft;
const float tr = config->cornerRadius.topRight;
const float bl = config->cornerRadius.bottomLeft;
const float br = config->cornerRadius.bottomRight;
if (config->width.left > 0) {
rectfill_wh(buffer, box.x, box.y + tl, config->width.left, box.height-tl-bl, ALLEGCOLOR(config->color));
/* Top-left half-arc */
if (tl > 0) {
CLIPPED_OPERATION(buffer, box.x, box.y+tl/2, tl, tl/2, {
arc_thickness(buffer, box.x+tl, box.y+tl, itofix(64), itofix(128), tl, ALLEGCOLOR(config->color), config->width.left);
});
}
/* Bottom-left half-arc */
if (bl > 0) {
const int y = box.y+box.height-bl;
CLIPPED_OPERATION(buffer, box.x, y, bl, bl/2, {
arc_thickness(buffer, box.x+bl, y-1, itofix(128), itofix(192), bl, ALLEGCOLOR(config->color), config->width.left);
});
}
}
if (config->width.right > 0) {
rectfill_wh(buffer, box.x+box.width-config->width.right, box.y+tr, config->width.right, box.height-br-tr, ALLEGCOLOR(config->color));
/* Top-right half-arc */
if (tr > 0) {
const int x = box.x+box.width-tr;
CLIPPED_OPERATION(buffer, x, box.y+tr/2, tr, tr/2, {
arc_thickness(buffer, x-1, box.y+tr, itofix(0), itofix(64), tr, ALLEGCOLOR(config->color), config->width.right);
});
}
/* Bottom-right half-arc */
if (br > 0) {
const int y = box.y+box.height-br;
const int x = box.x+box.width-br;
CLIPPED_OPERATION(buffer, x, y, br, br/2, {
arc_thickness(buffer, x-1, y-1, itofix(192), itofix(256), br, ALLEGCOLOR(config->color), config->width.right);
});
}
}
if (config->width.top > 0) {
rectfill_wh(buffer, box.x + tl, box.y, box.width - tr - tl, config->width.top, ALLEGCOLOR(config->color));
/* Top-left half-arc */
if (tl > 0) {
CLIPPED_OPERATION(buffer, box.x, box.y, tl, tl/2, {
arc_thickness(buffer, box.x+tl, box.y+tl, itofix(64), itofix(128), tl, ALLEGCOLOR(config->color), config->width.top);
});
}
/* Top-right half-arc */
if (tr > 0) {
const int x = box.x+box.width-tr;
CLIPPED_OPERATION(buffer, x, box.y, tr, tr/2, {
arc_thickness(buffer, x-1, box.y+tr, itofix(0), itofix(64), tr, ALLEGCOLOR(config->color), config->width.top);
});
}
}
if (config->width.bottom > 0) {
rectfill_wh(buffer, box.x+bl, box.y+box.height-config->width.bottom, box.width-br-bl, config->width.bottom, ALLEGCOLOR(config->color));
/* Bottom-left half-arc */
if (bl > 0) {
const int y = box.y+box.height-bl;
CLIPPED_OPERATION(buffer, box.x, y+bl/2, bl, bl/2, {
arc_thickness(buffer, box.x+bl, y-1, itofix(128), itofix(192), bl, ALLEGCOLOR(config->color), config->width.bottom);
});
}
/* Bottom-right half-arc */
if (br > 0) {
const int y = box.y+box.height-br;
const int x = box.x+box.width-br;
CLIPPED_OPERATION(buffer, x, y+br/2, br, br/2, {
arc_thickness(buffer, x-1, y-1, itofix(192), itofix(256), br, ALLEGCOLOR(config->color), config->width.bottom);
});
}
}
break;
}
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
Clay_CustomRenderData *config = &renderCommand->renderData.custom;
alleg4_custom_element *callback_info = (alleg4_custom_element *)config->customData;
callback_info->render(buffer, box, callback_info->user_data);
break;
}
}
}
}
Clay_Dimensions alleg4_measure_text(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData) {
FONT *font_object = get_font(config->fontId);
char *slice = (char *)calloc(text.length + 1, 1);
memcpy(slice, text.chars, text.length);
const Clay_Dimensions d = (Clay_Dimensions) {
.width = text_length(font_object, slice),
.height = text_height(font_object)
};
free(slice);
return d;
}

28
renderers/alleg4/alleg4.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef CLAY_ALLEG4_HEADER
#define CLAY_ALLEG4_HEADER
#include "clay.h"
#include <allegro.h>
#define ALLEGCOLOR(COLOR) makecol(COLOR.r, COLOR.g, COLOR.b)
typedef struct {
void (*render)(BITMAP *buffer, Clay_BoundingBox box, void *user_data);
void *user_data;
} alleg4_custom_element;
void alleg4_init_fonts(size_t);
void alleg4_set_font(unsigned int, FONT *);
void alleg4_render(
BITMAP *buffer,
Clay_RenderCommandArray renderCommands
);
Clay_Dimensions alleg4_measure_text(
Clay_StringSlice text,
Clay_TextElementConfig *config,
void *userData
);
#endif