Fix faders, z indexing, svg rendering
This commit is contained in:
399
assets/fader.svg
Normal file
399
assets/fader.svg
Normal file
@@ -0,0 +1,399 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
sodipodi:docname="fader.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="4.1585017"
|
||||
inkscape:cx="355.53671"
|
||||
inkscape:cy="395.21446"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1369"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<linearGradient
|
||||
id="linearGradient7718"
|
||||
y2="528.75"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="87.865997"
|
||||
gradientTransform="matrix(1.0278,0,0,1,787.52,-27.904)"
|
||||
y1="516.83002"
|
||||
x1="87.865997"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3899-3"
|
||||
style="stop-color:#999999"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3901-5"
|
||||
style="stop-color:#999999;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7720"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1.0235,0,0,1,242.38,-1008.6)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-4"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-44"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7722"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1,0,0,0.50643,256.46,265.42)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-5-0"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-2-7"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7724"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1,0,0,0.42746,256.46,317.38)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-2-9"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-7-3"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7726"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1,0,0,0.26952,256.46,405.1)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-3-1"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-29-29"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7728"
|
||||
y2="521.41998"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="87.865997"
|
||||
gradientTransform="matrix(1.0364,0,0,0.96441,786.64,-1114.5)"
|
||||
y1="516.83002"
|
||||
x1="87.865997"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop4107-6"
|
||||
style="stop-color:#999999"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop4109-0"
|
||||
style="stop-color:#333333"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7730"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1.0364,0,0,0.96441,234.39,-1076.7)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-0-3"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-5-04"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7732"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1.0274,0,0,0.48841,240.25,-833.5)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-5-6-12"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-2-3-5"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7734"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1.0213,0,0,0.41225,243.85,-783.64)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-2-5-8"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-7-4-1"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient7736"
|
||||
y2="499.10001"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x2="618.48999"
|
||||
gradientTransform="matrix(1.0122,0,0,0.25993,249.26,-698.66)"
|
||||
y1="496.57001"
|
||||
x1="618.48999"
|
||||
inkscape:collect="always">
|
||||
<stop
|
||||
id="stop3925-3-8-82"
|
||||
style="stop-color:#cccccc"
|
||||
offset="0" />
|
||||
<stop
|
||||
id="stop3927-29-9-9"
|
||||
style="stop-color:#cccccc;stop-opacity:0"
|
||||
offset="1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g6784"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,-142.0892,-39.104459)">
|
||||
<rect
|
||||
id="rect3827-6"
|
||||
style="fill:#4d4d4d"
|
||||
rx="3"
|
||||
ry="3"
|
||||
height="129.28999"
|
||||
width="62.143002"
|
||||
y="488.03"
|
||||
x="847.03003" />
|
||||
<rect
|
||||
id="rect3829-6"
|
||||
style="fill:#e6e6e6"
|
||||
height="3.5355"
|
||||
width="60.418999"
|
||||
y="552.41998"
|
||||
x="847.96997" />
|
||||
<rect
|
||||
id="rect3831-4"
|
||||
style="fill:url(#linearGradient7718)"
|
||||
rx="2.1482999"
|
||||
ry="2"
|
||||
height="21.070999"
|
||||
width="60.612999"
|
||||
y="488.73999"
|
||||
x="847.71997" />
|
||||
<rect
|
||||
id="rect3829-4-0"
|
||||
style="fill:#333333"
|
||||
height="10.119"
|
||||
width="58.811001"
|
||||
y="540.26001"
|
||||
x="847.91998" />
|
||||
<rect
|
||||
id="rect3829-47-8"
|
||||
style="fill:#333333"
|
||||
height="8.7929001"
|
||||
width="61.132999"
|
||||
y="530.89001"
|
||||
x="847.03003" />
|
||||
<rect
|
||||
id="rect3829-3-70"
|
||||
style="fill:#1a1a1a"
|
||||
height="4.5457001"
|
||||
width="61.132999"
|
||||
y="512.47998"
|
||||
x="847.03003" />
|
||||
<rect
|
||||
id="rect3829-3-7-6"
|
||||
style="fill:#1a1a1a"
|
||||
height="11.364"
|
||||
width="61.132999"
|
||||
y="518.25"
|
||||
x="847.03003" />
|
||||
<rect
|
||||
id="rect3829-9-9"
|
||||
ry="0.63958001"
|
||||
style="fill:url(#linearGradient7720)"
|
||||
rx="1.0235"
|
||||
transform="scale(1,-1)"
|
||||
height="2.2612"
|
||||
width="60.012001"
|
||||
y="-511.76001"
|
||||
x="847.71997" />
|
||||
<rect
|
||||
id="rect3829-9-0-07"
|
||||
style="fill:url(#linearGradient7722)"
|
||||
rx="1"
|
||||
ry="0.32390001"
|
||||
height="1.1452"
|
||||
width="58.632999"
|
||||
y="517.03003"
|
||||
x="847.89001" />
|
||||
<rect
|
||||
id="rect3829-9-2-4"
|
||||
style="fill:url(#linearGradient7724)"
|
||||
rx="1"
|
||||
ry="0.27340001"
|
||||
height="0.9666"
|
||||
width="58.632999"
|
||||
y="529.76001"
|
||||
x="847.89001" />
|
||||
<rect
|
||||
id="rect3829-9-07-1"
|
||||
style="fill:url(#linearGradient7726)"
|
||||
rx="1"
|
||||
ry="0.17238"
|
||||
height="0.60946"
|
||||
width="58.632999"
|
||||
y="539.01001"
|
||||
x="847.89001" />
|
||||
<rect
|
||||
id="rect3831-5-8"
|
||||
style="fill:url(#linearGradient7728)"
|
||||
transform="scale(1,-1)"
|
||||
rx="2.1661999"
|
||||
ry="1.9288"
|
||||
height="20.320999"
|
||||
width="61.118"
|
||||
y="-616.23999"
|
||||
x="847.34003" />
|
||||
<rect
|
||||
id="rect3829-4-1-2"
|
||||
style="fill:#333333"
|
||||
transform="scale(1,-1)"
|
||||
height="9.7593002"
|
||||
width="58.811001"
|
||||
y="-567.92999"
|
||||
x="847.91998" />
|
||||
<rect
|
||||
id="rect3829-47-5-6"
|
||||
style="fill:#666666"
|
||||
transform="scale(1,-1)"
|
||||
height="8.4799004"
|
||||
width="61.132999"
|
||||
y="-576.96002"
|
||||
x="847.03003" />
|
||||
<rect
|
||||
id="rect3829-3-0-9"
|
||||
style="fill:#808080"
|
||||
transform="scale(1,-1)"
|
||||
height="4.3839002"
|
||||
width="61.132999"
|
||||
y="-594.71997"
|
||||
x="847.03003" />
|
||||
<rect
|
||||
id="rect3829-3-7-0-2"
|
||||
style="fill:#808080"
|
||||
transform="scale(1,-1)"
|
||||
height="10.96"
|
||||
width="61.132999"
|
||||
y="-589.15997"
|
||||
x="847.03003" />
|
||||
<rect
|
||||
id="rect3829-9-04-2"
|
||||
style="fill:url(#linearGradient7730)"
|
||||
transform="scale(1,-1)"
|
||||
rx="1.0364"
|
||||
ry="0.61681002"
|
||||
height="2.1808"
|
||||
width="60.766998"
|
||||
y="-597.59998"
|
||||
x="847.34003" />
|
||||
<rect
|
||||
id="rect3829-9-0-0-1"
|
||||
style="fill:url(#linearGradient7732)"
|
||||
transform="scale(1,-1)"
|
||||
rx="1.0274"
|
||||
ry="0.31237"
|
||||
height="1.1044"
|
||||
width="60.240002"
|
||||
y="-590.84003"
|
||||
x="847.89001" />
|
||||
<rect
|
||||
id="rect3829-9-2-7-2"
|
||||
style="fill:url(#linearGradient7734)"
|
||||
transform="scale(1,-1)"
|
||||
rx="1.0213"
|
||||
ry="0.26366001"
|
||||
height="0.93220001"
|
||||
width="59.882999"
|
||||
y="-578.82001"
|
||||
x="847.89001" />
|
||||
<rect
|
||||
id="rect3829-9-07-5-1"
|
||||
style="fill:url(#linearGradient7736)"
|
||||
transform="scale(1,-1)"
|
||||
rx="1.0122"
|
||||
ry="0.16624001"
|
||||
height="0.58775997"
|
||||
width="59.347"
|
||||
y="-569.52002"
|
||||
x="847.89001" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
8
build.c
8
build.c
@@ -88,7 +88,7 @@ static bool build_lunasvg_lib(const char *build_dir, bool debug) {
|
||||
Nob_Cmd cmd = {0};
|
||||
nob_cmd_append(&cmd, "clang");
|
||||
nob_cmd_append(&cmd, "-std=c11", "-c");
|
||||
nob_cmd_append(&cmd, "-DPLUTVOG_BUILD", "-DPLUTVOG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "-DPLUTOVG_BUILD", "-DPLUTOVG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/include");
|
||||
nob_cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/source");
|
||||
nob_cmd_append(&cmd, "-Wall", "-Wno-unused-function", "-Wno-unused-parameter");
|
||||
@@ -118,7 +118,7 @@ static bool build_lunasvg_lib(const char *build_dir, bool debug) {
|
||||
nob_cmd_append(&cmd, "clang++");
|
||||
nob_cmd_append(&cmd, "-std=c++17", "-c");
|
||||
nob_cmd_append(&cmd, "-fno-exceptions", "-fno-rtti");
|
||||
nob_cmd_append(&cmd, "-DLUNASVG_BUILD", "-DLUNASVG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "-DLUNASVG_BUILD", "-DLUNASVG_BUILD_STATIC", "-DPLUTOVG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "-Ivendor/lunasvg/include");
|
||||
nob_cmd_append(&cmd, "-Ivendor/lunasvg/source");
|
||||
nob_cmd_append(&cmd, "-Ivendor/lunasvg/plutovg/include");
|
||||
@@ -346,7 +346,7 @@ static bool build_lunasvg_lib(const char *build_dir, bool debug) {
|
||||
Nob_Cmd cmd = {0};
|
||||
nob_cmd_append(&cmd, "cl.exe", "/nologo", "/c");
|
||||
nob_cmd_append(&cmd, "/std:c11");
|
||||
nob_cmd_append(&cmd, "/DPLUTVOG_BUILD", "/DPLUTVOG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "/DPLUTOVG_BUILD", "/DPLUTOVG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/include");
|
||||
nob_cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/source");
|
||||
nob_cmd_append(&cmd, "/W3");
|
||||
@@ -367,7 +367,7 @@ static bool build_lunasvg_lib(const char *build_dir, bool debug) {
|
||||
Nob_Cmd cmd = {0};
|
||||
nob_cmd_append(&cmd, "cl.exe", "/nologo", "/c");
|
||||
nob_cmd_append(&cmd, "/std:c++17", "/EHsc");
|
||||
nob_cmd_append(&cmd, "/DLUNASVG_BUILD", "/DLUNASVG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "/DLUNASVG_BUILD", "/DLUNASVG_BUILD_STATIC", "/DPLUTOVG_BUILD_STATIC");
|
||||
nob_cmd_append(&cmd, "/Ivendor/lunasvg/include");
|
||||
nob_cmd_append(&cmd, "/Ivendor/lunasvg/source");
|
||||
nob_cmd_append(&cmd, "/Ivendor/lunasvg/plutovg/include");
|
||||
|
||||
105
src/main.cpp
105
src/main.cpp
@@ -115,6 +115,11 @@ struct AppState {
|
||||
F32 demo_slider_v;
|
||||
F32 demo_fader;
|
||||
|
||||
// Scrollbar drag state
|
||||
B32 scrollbar_dragging;
|
||||
F32 scrollbar_drag_start_y;
|
||||
F32 scrollbar_drag_start_scroll;
|
||||
|
||||
// Audio device selection
|
||||
S32 audio_device_sel; // dropdown index: 0 = None, 1+ = device
|
||||
S32 audio_device_prev; // previous selection for change detection
|
||||
@@ -175,13 +180,20 @@ static void build_main_panel(AppState *app) {
|
||||
ui_tab_bar("MainTabRow", main_tabs, 1, &sel);
|
||||
}
|
||||
|
||||
CLAY(CLAY_ID("MainScrollArea"),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
||||
.layoutDirection = CLAY_LEFT_TO_RIGHT,
|
||||
}
|
||||
) {
|
||||
CLAY(CLAY_ID("MainContent"),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_GROW() },
|
||||
.padding = { uip(16), uip(16), uip(12), uip(12) },
|
||||
.childGap = uip(12),
|
||||
.layoutDirection = CLAY_TOP_TO_BOTTOM,
|
||||
}
|
||||
},
|
||||
.clip = { .vertical = true, .childOffset = Clay_GetScrollOffset() },
|
||||
) {
|
||||
// Top highlight (beveled edge)
|
||||
CLAY(CLAY_ID("MainHighlight"),
|
||||
@@ -353,6 +365,97 @@ static void build_main_panel(AppState *app) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scrollbar
|
||||
{
|
||||
Clay_ScrollContainerData scroll_data = Clay_GetScrollContainerData(CLAY_ID("MainContent"));
|
||||
if (scroll_data.found && scroll_data.contentDimensions.height > scroll_data.scrollContainerDimensions.height) {
|
||||
float track_h = scroll_data.scrollContainerDimensions.height;
|
||||
float content_h = scroll_data.contentDimensions.height;
|
||||
float visible_ratio = track_h / content_h;
|
||||
float thumb_h = Max(visible_ratio * track_h, uis(24));
|
||||
float scroll_range = content_h - track_h;
|
||||
float scroll_pct = scroll_range > 0 ? -scroll_data.scrollPosition->y / scroll_range : 0;
|
||||
float thumb_y = scroll_pct * (track_h - thumb_h);
|
||||
float bar_w = uis(8);
|
||||
|
||||
// Handle scrollbar drag
|
||||
Clay_ElementId thumb_id = CLAY_ID("MainScrollThumb");
|
||||
Clay_ElementId track_id = CLAY_ID("MainScrollTrack");
|
||||
B32 thumb_hovered = Clay_PointerOver(thumb_id);
|
||||
B32 track_hovered = Clay_PointerOver(track_id);
|
||||
PlatformInput input = g_wstate.input;
|
||||
B32 mouse_clicked = input.mouse_down && !input.was_mouse_down;
|
||||
|
||||
if (mouse_clicked && thumb_hovered) {
|
||||
app->scrollbar_dragging = true;
|
||||
app->scrollbar_drag_start_y = input.mouse_pos.y;
|
||||
app->scrollbar_drag_start_scroll = scroll_data.scrollPosition->y;
|
||||
} else if (mouse_clicked && track_hovered && !thumb_hovered) {
|
||||
// Click on track: jump scroll position so thumb centers on click
|
||||
Clay_BoundingBox track_bb = Clay_GetElementData(track_id).boundingBox;
|
||||
float click_rel = input.mouse_pos.y - track_bb.y;
|
||||
float target_pct = (click_rel - thumb_h / 2) / (track_h - thumb_h);
|
||||
if (target_pct < 0) target_pct = 0;
|
||||
if (target_pct > 1) target_pct = 1;
|
||||
scroll_data.scrollPosition->y = -target_pct * scroll_range;
|
||||
// Start dragging from new position
|
||||
app->scrollbar_dragging = true;
|
||||
app->scrollbar_drag_start_y = input.mouse_pos.y;
|
||||
app->scrollbar_drag_start_scroll = scroll_data.scrollPosition->y;
|
||||
}
|
||||
|
||||
if (!input.mouse_down) {
|
||||
app->scrollbar_dragging = false;
|
||||
}
|
||||
|
||||
if (app->scrollbar_dragging) {
|
||||
float dy = input.mouse_pos.y - app->scrollbar_drag_start_y;
|
||||
float scroll_per_px = scroll_range / (track_h - thumb_h);
|
||||
float new_scroll = app->scrollbar_drag_start_scroll - dy * scroll_per_px;
|
||||
if (new_scroll > 0) new_scroll = 0;
|
||||
if (new_scroll < -scroll_range) new_scroll = -scroll_range;
|
||||
scroll_data.scrollPosition->y = new_scroll;
|
||||
}
|
||||
|
||||
// Thumb color: highlight on hover or drag
|
||||
Clay_Color thumb_color = g_theme.scrollbar_grab;
|
||||
if (app->scrollbar_dragging || thumb_hovered) {
|
||||
thumb_color = Clay_Color{
|
||||
(float)Min((int)thumb_color.r + 30, 255),
|
||||
(float)Min((int)thumb_color.g + 30, 255),
|
||||
(float)Min((int)thumb_color.b + 30, 255),
|
||||
thumb_color.a
|
||||
};
|
||||
}
|
||||
|
||||
CLAY(track_id,
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(bar_w), .height = CLAY_SIZING_GROW() },
|
||||
},
|
||||
.backgroundColor = g_theme.scrollbar_bg
|
||||
) {
|
||||
CLAY(thumb_id,
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(thumb_h) },
|
||||
},
|
||||
.backgroundColor = thumb_color,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS),
|
||||
.floating = {
|
||||
.offset = { 0, thumb_y },
|
||||
.attachPoints = {
|
||||
.element = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
},
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
},
|
||||
) {}
|
||||
}
|
||||
} else {
|
||||
app->scrollbar_dragging = false;
|
||||
}
|
||||
}
|
||||
} // MainScrollArea
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ static LRESULT CALLBACK win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
|
||||
case WM_MOUSEWHEEL:
|
||||
if (g_current_window) {
|
||||
int16_t wheel_delta = (int16_t)HIWORD(wparam);
|
||||
g_current_window->input.scroll_delta.y += (F32)wheel_delta / (F32)WHEEL_DELTA;
|
||||
g_current_window->input.scroll_delta.y += (F32)wheel_delta / (F32)WHEEL_DELTA * 6.0f;
|
||||
}
|
||||
return 0;
|
||||
case WM_COMMAND:
|
||||
|
||||
@@ -27,5 +27,5 @@ void renderer_set_clear_color(Renderer *renderer, float r, float g, float b
|
||||
struct Vec2F32;
|
||||
Vec2F32 renderer_measure_text(const char *text, int32_t length, float font_size, void *user_data);
|
||||
|
||||
// Upload an R8 icon atlas texture for icon rendering
|
||||
// Upload an RGBA8 icon atlas texture for icon rendering (4 bytes per pixel)
|
||||
void renderer_create_icon_atlas(Renderer *renderer, const uint8_t *data, int32_t w, int32_t h);
|
||||
|
||||
@@ -118,8 +118,12 @@ float rounded_rect_sdf(float2 sample_pos, float2 rect_center, float2 rect_half_s
|
||||
float4 PSMain(PSInput input) : SV_TARGET {
|
||||
float4 col = input.col;
|
||||
|
||||
if (input.mode > 0.5) {
|
||||
// Textured glyph mode: sample font atlas alpha
|
||||
if (input.mode > 1.5) {
|
||||
// RGBA textured mode: sample all channels, multiply by vertex color
|
||||
float4 tex = font_tex.Sample(font_smp, input.uv);
|
||||
col *= tex;
|
||||
} else if (input.mode > 0.5) {
|
||||
// Alpha-only textured mode: sample R channel as alpha (font atlas)
|
||||
float alpha = font_tex.Sample(font_smp, input.uv).r;
|
||||
col.a *= alpha;
|
||||
} else {
|
||||
@@ -210,6 +214,11 @@ struct Renderer {
|
||||
HDC measure_dc;
|
||||
HFONT measure_font;
|
||||
F32 measure_font_size;
|
||||
|
||||
// Clear color
|
||||
float clear_r = 0.12f;
|
||||
float clear_g = 0.12f;
|
||||
float clear_b = 0.13f;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -914,7 +923,7 @@ static void emit_quad_rotated(DrawBatch *batch,
|
||||
v[i].corner_radii[2] = 0; v[i].corner_radii[3] = 0;
|
||||
v[i].border_thickness = 0;
|
||||
v[i].softness = 0;
|
||||
v[i].mode = 1.0f;
|
||||
v[i].mode = 2.0f;
|
||||
}
|
||||
|
||||
U32 *idx = &batch->indices[batch->index_count];
|
||||
@@ -1175,7 +1184,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
r->command_list->ResourceBarrier(1, &barrier);
|
||||
|
||||
const float clear_color[4] = { 0.12f, 0.12f, 0.13f, 1.0f };
|
||||
const float clear_color[4] = { r->clear_r, r->clear_g, r->clear_b, 1.0f };
|
||||
r->command_list->ClearRenderTargetView(r->rtv_descriptors[back_buffer_idx], clear_color, 0, nullptr);
|
||||
r->command_list->OMSetRenderTargets(1, &r->rtv_descriptors[back_buffer_idx], FALSE, nullptr);
|
||||
|
||||
@@ -1202,14 +1211,15 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
|
||||
auto bind_font = [&]() {
|
||||
if (bound_texture != 0) {
|
||||
flush_batch(r, &batch, buf_idx, &flush_index_start, r->srv_heap);
|
||||
ID3D12DescriptorHeap *heap = bound_texture == 1 ? r->icon_srv_heap : r->srv_heap;
|
||||
flush_batch(r, &batch, buf_idx, &flush_index_start, heap);
|
||||
bound_texture = 0;
|
||||
}
|
||||
};
|
||||
|
||||
auto bind_icon = [&]() {
|
||||
if (bound_texture != 1 && r->icon_srv_heap) {
|
||||
flush_batch(r, &batch, buf_idx, &flush_index_start);
|
||||
flush_batch(r, &batch, buf_idx, &flush_index_start, r->srv_heap);
|
||||
bound_texture = 1;
|
||||
}
|
||||
};
|
||||
@@ -1330,7 +1340,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
cr, cg, cb, ca,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 1.0f);
|
||||
0, 0, 2.0f);
|
||||
} else if (type == CUSTOM_RENDER_ROTATED_ICON) {
|
||||
bind_icon();
|
||||
CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData;
|
||||
@@ -1383,7 +1393,7 @@ void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int
|
||||
tex_desc.Height = h;
|
||||
tex_desc.DepthOrArraySize = 1;
|
||||
tex_desc.MipLevels = 1;
|
||||
tex_desc.Format = DXGI_FORMAT_R8_UNORM;
|
||||
tex_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
tex_desc.SampleDesc.Count = 1;
|
||||
tex_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
|
||||
@@ -1418,7 +1428,7 @@ void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int
|
||||
upload_buf->Map(0, &read_range, &mapped);
|
||||
U8 *dst = (U8 *)mapped;
|
||||
for (int y = 0; y < h; y++) {
|
||||
memcpy(dst + y * footprint.Footprint.RowPitch, data + y * w, w);
|
||||
memcpy(dst + y * footprint.Footprint.RowPitch, data + y * w * 4, w * 4);
|
||||
}
|
||||
upload_buf->Unmap(0, nullptr);
|
||||
|
||||
@@ -1458,7 +1468,7 @@ void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int
|
||||
r->device->CreateDescriptorHeap(&srv_desc, IID_PPV_ARGS(&r->icon_srv_heap));
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srv_view = {};
|
||||
srv_view.Format = DXGI_FORMAT_R8_UNORM;
|
||||
srv_view.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srv_view.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_view.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
srv_view.Texture2D.MipLevels = 1;
|
||||
@@ -1482,3 +1492,17 @@ void renderer_resize(Renderer *r, int32_t width, int32_t height) {
|
||||
r->width = width;
|
||||
r->height = height;
|
||||
}
|
||||
|
||||
void renderer_set_clear_color(Renderer *r, float cr, float cg, float cb) {
|
||||
r->clear_r = cr;
|
||||
r->clear_g = cg;
|
||||
r->clear_b = cb;
|
||||
}
|
||||
|
||||
void renderer_set_font_scale(Renderer *r, float scale) {
|
||||
float target_size = 15.0f * scale;
|
||||
if (fabsf(target_size - r->font_atlas_size) < 0.1f) return;
|
||||
wait_for_pending(r);
|
||||
if (r->font_texture) { r->font_texture->Release(); r->font_texture = nullptr; }
|
||||
create_font_atlas(r, target_size);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,10 @@ fragment float4 fragment_main(Fragment in [[stage_in]],
|
||||
sampler font_smp [[sampler(0)]]) {
|
||||
float4 col = in.col;
|
||||
|
||||
if (in.mode > 0.5) {
|
||||
if (in.mode > 1.5) {
|
||||
float4 tex = font_tex.sample(font_smp, in.uv);
|
||||
col *= tex;
|
||||
} else if (in.mode > 0.5) {
|
||||
float alpha = font_tex.sample(font_smp, in.uv).r;
|
||||
col.a *= alpha;
|
||||
} else {
|
||||
@@ -447,7 +450,7 @@ static void emit_quad_rotated(DrawBatch *batch,
|
||||
v[i].corner_radii[2] = 0; v[i].corner_radii[3] = 0;
|
||||
v[i].border_thickness = 0;
|
||||
v[i].softness = 0;
|
||||
v[i].mode = 1.0f; // textured
|
||||
v[i].mode = 2.0f; // RGBA textured
|
||||
}
|
||||
|
||||
U32 *idx = &batch->indices[batch->index_count];
|
||||
@@ -901,7 +904,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
cr, cg, cb, ca,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 1.0f);
|
||||
0, 0, 2.0f);
|
||||
} else if (type == CUSTOM_RENDER_ROTATED_ICON) {
|
||||
bind_icon_texture();
|
||||
CustomRotatedIconData *ri = (CustomRotatedIconData *)custom->customData;
|
||||
@@ -943,7 +946,7 @@ void renderer_end_frame(Renderer *r, Clay_RenderCommandArray render_commands) {
|
||||
|
||||
void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int32_t h) {
|
||||
MTLTextureDescriptor *tex_desc = [[MTLTextureDescriptor alloc] init];
|
||||
tex_desc.pixelFormat = MTLPixelFormatR8Unorm;
|
||||
tex_desc.pixelFormat = MTLPixelFormatRGBA8Unorm;
|
||||
tex_desc.width = w;
|
||||
tex_desc.height = h;
|
||||
tex_desc.usage = MTLTextureUsageShaderRead;
|
||||
@@ -952,7 +955,7 @@ void renderer_create_icon_atlas(Renderer *r, const uint8_t *data, int32_t w, int
|
||||
[r->icon_texture replaceRegion:MTLRegionMake2D(0, 0, w, h)
|
||||
mipmapLevel:0
|
||||
withBytes:data
|
||||
bytesPerRow:w];
|
||||
bytesPerRow:w * 4];
|
||||
}
|
||||
|
||||
void renderer_set_clear_color(Renderer *r, float cr, float cg, float cb) {
|
||||
|
||||
@@ -204,7 +204,7 @@ void ui_begin_frame(UI_Context *ctx, F32 viewport_w, F32 viewport_h,
|
||||
Clay_SetCurrentContext(ctx->clay_ctx);
|
||||
Clay_SetLayoutDimensions(Clay_Dimensions{viewport_w, viewport_h});
|
||||
Clay_SetPointerState(Clay_Vector2{mouse_pos.x, mouse_pos.y}, mouse_down != 0);
|
||||
Clay_UpdateScrollContainers(true, Clay_Vector2{scroll_delta.x, scroll_delta.y}, dt);
|
||||
Clay_UpdateScrollContainers(false, Clay_Vector2{scroll_delta.x, scroll_delta.y}, dt);
|
||||
Clay_BeginLayout();
|
||||
}
|
||||
|
||||
|
||||
@@ -170,8 +170,11 @@ struct CustomRotatedIconData {
|
||||
#define WIDGET_SLIDER_V_THUMB_H uis(14)
|
||||
#define WIDGET_FADER_HEIGHT uis(160)
|
||||
#define WIDGET_FADER_TRACK_W uis(4)
|
||||
#define WIDGET_FADER_CAP_W uis(38)
|
||||
#define WIDGET_FADER_CAP_W uis(25)
|
||||
#define WIDGET_FADER_CAP_H uis(52)
|
||||
#define WIDGET_FADER_TICK_MAJOR_W uis(8)
|
||||
#define WIDGET_FADER_TICK_MINOR_W uis(4)
|
||||
#define WIDGET_FADER_TICK_H uis(1)
|
||||
|
||||
////////////////////////////////
|
||||
// Corner radius (from theme)
|
||||
|
||||
@@ -30,30 +30,80 @@ static const char *g_icon_svgs[UI_ICON_COUNT] = {
|
||||
<line x1="12" y1="12" x2="12" y2="3" stroke="white" stroke-width="2.5" stroke-linecap="round"/>
|
||||
</svg>)",
|
||||
|
||||
// UI_ICON_SLIDER_THUMB - layered body with grip ridges
|
||||
// UI_ICON_SLIDER_THUMB - solid body with grip ridges
|
||||
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<rect x="2" y="1" width="20" height="22" rx="4" fill="white" opacity="0.15"/>
|
||||
<rect x="3" y="2" width="18" height="20" rx="3" fill="white" opacity="0.55"/>
|
||||
<rect x="4" y="3" width="16" height="1" rx="0.5" fill="white" opacity="0.3"/>
|
||||
<line x1="6" y1="8" x2="18" y2="8" stroke="white" stroke-width="1" opacity="0.8"/>
|
||||
<line x1="6" y1="11" x2="18" y2="11" stroke="white" stroke-width="1" opacity="0.8"/>
|
||||
<line x1="6" y1="14" x2="18" y2="14" stroke="white" stroke-width="1" opacity="0.8"/>
|
||||
<line x1="6" y1="17" x2="18" y2="17" stroke="white" stroke-width="1" opacity="0.8"/>
|
||||
<rect x="2" y="1" width="20" height="22" rx="4" fill="white"/>
|
||||
<line x1="6" y1="8" x2="18" y2="8" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
||||
<line x1="6" y1="11" x2="18" y2="11" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
||||
<line x1="6" y1="14" x2="18" y2="14" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
||||
<line x1="6" y1="17" x2="18" y2="17" stroke="black" stroke-width="1.2" opacity="0.3"/>
|
||||
</svg>)",
|
||||
|
||||
// UI_ICON_FADER - Pro Tools-style fader cap: solid body, bright center indicator, beveled caps
|
||||
R"(<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 48">
|
||||
<rect x="1" y="0" width="22" height="48" rx="3" fill="white" opacity="0.7"/>
|
||||
<rect x="2" y="1" width="20" height="5" rx="2" fill="white" opacity="0.85"/>
|
||||
<rect x="2" y="1" width="20" height="1.5" rx="0.5" fill="white" opacity="0.95"/>
|
||||
<line x1="5" y1="10" x2="19" y2="10" stroke="black" stroke-width="0.5" opacity="0.2"/>
|
||||
<line x1="5" y1="14" x2="19" y2="14" stroke="black" stroke-width="0.5" opacity="0.2"/>
|
||||
<rect x="1" y="22" width="22" height="4" rx="0.5" fill="white" opacity="1.0"/>
|
||||
<line x1="5" y1="30" x2="19" y2="30" stroke="black" stroke-width="0.5" opacity="0.2"/>
|
||||
<line x1="5" y1="34" x2="19" y2="34" stroke="black" stroke-width="0.5" opacity="0.2"/>
|
||||
<rect x="2" y="42" width="20" height="5" rx="2" fill="white" opacity="0.85"/>
|
||||
<rect x="2" y="45.5" width="20" height="1.5" rx="0.5" fill="white" opacity="0.6"/>
|
||||
</svg>)",
|
||||
// UI_ICON_FADER - exact asset fader cap from assets/fader.svg
|
||||
R"SVG(<svg xmlns="http://www.w3.org/2000/svg" viewBox="847 488 62.2 129.3">
|
||||
<defs>
|
||||
<linearGradient id="linearGradient7718" y2="528.75" gradientUnits="userSpaceOnUse" x2="87.866" gradientTransform="matrix(1.0278,0,0,1,787.52,-27.904)" y1="516.83" x1="87.866">
|
||||
<stop style="stop-color:#999999" offset="0"/>
|
||||
<stop style="stop-color:#999999;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7720" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0235,0,0,1,242.38,-1008.6)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7722" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1,0,0,0.50643,256.46,265.42)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7724" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1,0,0,0.42746,256.46,317.38)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7726" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1,0,0,0.26952,256.46,405.1)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7728" y2="521.42" gradientUnits="userSpaceOnUse" x2="87.866" gradientTransform="matrix(1.0364,0,0,0.96441,786.64,-1114.5)" y1="516.83" x1="87.866">
|
||||
<stop style="stop-color:#999999" offset="0"/>
|
||||
<stop style="stop-color:#333333" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7730" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0364,0,0,0.96441,234.39,-1076.7)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7732" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0274,0,0,0.48841,240.25,-833.5)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7734" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0213,0,0,0.41225,243.85,-783.64)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linearGradient7736" y2="499.1" gradientUnits="userSpaceOnUse" x2="618.49" gradientTransform="matrix(1.0122,0,0,0.25993,249.26,-698.66)" y1="496.57" x1="618.49">
|
||||
<stop style="stop-color:#cccccc" offset="0"/>
|
||||
<stop style="stop-color:#cccccc;stop-opacity:0" offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect style="fill:#4d4d4d" rx="3" ry="3" height="129.29" width="62.143" y="488.03" x="847.03"/>
|
||||
<rect style="fill:#e6e6e6" height="3.5355" width="60.419" y="552.42" x="847.97"/>
|
||||
<rect style="fill:url(#linearGradient7718)" rx="2.148" ry="2" height="21.071" width="60.613" y="488.74" x="847.72"/>
|
||||
<rect style="fill:#333333" height="10.119" width="58.811" y="540.26" x="847.92"/>
|
||||
<rect style="fill:#333333" height="8.793" width="61.133" y="530.89" x="847.03"/>
|
||||
<rect style="fill:#1a1a1a" height="4.546" width="61.133" y="512.48" x="847.03"/>
|
||||
<rect style="fill:#1a1a1a" height="11.364" width="61.133" y="518.25" x="847.03"/>
|
||||
<rect style="fill:url(#linearGradient7720)" rx="1.024" ry="0.64" transform="scale(1,-1)" height="2.261" width="60.012" y="-511.76" x="847.72"/>
|
||||
<rect style="fill:url(#linearGradient7722)" rx="1" ry="0.324" height="1.145" width="58.633" y="517.03" x="847.89"/>
|
||||
<rect style="fill:url(#linearGradient7724)" rx="1" ry="0.273" height="0.967" width="58.633" y="529.76" x="847.89"/>
|
||||
<rect style="fill:url(#linearGradient7726)" rx="1" ry="0.172" height="0.609" width="58.633" y="539.01" x="847.89"/>
|
||||
<rect style="fill:url(#linearGradient7728)" transform="scale(1,-1)" rx="2.166" ry="1.929" height="20.321" width="61.118" y="-616.24" x="847.34"/>
|
||||
<rect style="fill:#333333" transform="scale(1,-1)" height="9.759" width="58.811" y="-567.93" x="847.92"/>
|
||||
<rect style="fill:#666666" transform="scale(1,-1)" height="8.48" width="61.133" y="-576.96" x="847.03"/>
|
||||
<rect style="fill:#808080" transform="scale(1,-1)" height="4.384" width="61.133" y="-594.72" x="847.03"/>
|
||||
<rect style="fill:#808080" transform="scale(1,-1)" height="10.96" width="61.133" y="-589.16" x="847.03"/>
|
||||
<rect style="fill:url(#linearGradient7730)" transform="scale(1,-1)" rx="1.036" ry="0.617" height="2.181" width="60.767" y="-597.6" x="847.34"/>
|
||||
<rect style="fill:url(#linearGradient7732)" transform="scale(1,-1)" rx="1.027" ry="0.312" height="1.104" width="60.24" y="-590.84" x="847.89"/>
|
||||
<rect style="fill:url(#linearGradient7734)" transform="scale(1,-1)" rx="1.021" ry="0.264" height="0.932" width="59.883" y="-578.82" x="847.89"/>
|
||||
<rect style="fill:url(#linearGradient7736)" transform="scale(1,-1)" rx="1.012" ry="0.166" height="0.588" width="59.347" y="-569.52" x="847.89"/>
|
||||
</svg>)SVG",
|
||||
};
|
||||
|
||||
U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) {
|
||||
@@ -65,7 +115,7 @@ U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) {
|
||||
if (atlas_w < 64) atlas_w = 64;
|
||||
if (atlas_h < 64) atlas_h = 64;
|
||||
|
||||
U8 *atlas = (U8 *)calloc(atlas_w * atlas_h, 1);
|
||||
U8 *atlas = (U8 *)calloc(atlas_w * atlas_h * 4, 1);
|
||||
if (!atlas) return nullptr;
|
||||
|
||||
S32 pen_x = 0;
|
||||
@@ -76,7 +126,7 @@ U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) {
|
||||
lunasvg::Bitmap bmp = doc->renderToBitmap(icon_size, icon_size);
|
||||
if (bmp.isNull()) continue;
|
||||
|
||||
// Extract alpha channel from ARGB32 premultiplied into R8
|
||||
// Copy BGRA premultiplied → RGBA straight (un-premultiply)
|
||||
U8 *src = bmp.data();
|
||||
S32 bmp_w = bmp.width();
|
||||
S32 bmp_h = bmp.height();
|
||||
@@ -84,9 +134,18 @@ U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size) {
|
||||
|
||||
for (S32 y = 0; y < bmp_h && y < atlas_h; y++) {
|
||||
for (S32 x = 0; x < bmp_w && (pen_x + x) < atlas_w; x++) {
|
||||
// ARGB32 premultiplied: bytes are B, G, R, A (little-endian)
|
||||
U8 a = src[y * stride + x * 4 + 3];
|
||||
atlas[y * atlas_w + pen_x + x] = a;
|
||||
U8 *s = &src[y * stride + x * 4];
|
||||
S32 dst_idx = (y * atlas_w + pen_x + x) * 4;
|
||||
U8 b = s[0], g = s[1], r = s[2], a = s[3];
|
||||
if (a > 0 && a < 255) {
|
||||
r = (U8)((r * 255) / a);
|
||||
g = (U8)((g * 255) / a);
|
||||
b = (U8)((b * 255) / a);
|
||||
}
|
||||
atlas[dst_idx + 0] = r;
|
||||
atlas[dst_idx + 1] = g;
|
||||
atlas[dst_idx + 2] = b;
|
||||
atlas[dst_idx + 3] = a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ struct UI_IconInfo {
|
||||
|
||||
extern UI_IconInfo g_icons[UI_ICON_COUNT];
|
||||
|
||||
// Rasterizes all icons into an R8 atlas bitmap.
|
||||
// Rasterizes all icons into an RGBA8 atlas bitmap (4 bytes per pixel).
|
||||
// Returns malloc'd data (caller frees). Sets *out_w, *out_h to atlas dimensions.
|
||||
// icon_size is the pixel height to rasterize each icon at.
|
||||
U8 *ui_icons_rasterize_atlas(S32 *out_w, S32 *out_h, S32 icon_size);
|
||||
|
||||
@@ -105,8 +105,8 @@ static void emit_shadow(Clay_BoundingBox bb, F32 ox, F32 oy, F32 radius,
|
||||
.backgroundColor = {0, 0, 0, per_layer},
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS + expand),
|
||||
.floating = {
|
||||
.parentId = parent_id,
|
||||
.offset = { -expand + ox, -expand + oy },
|
||||
.parentId = parent_id,
|
||||
.zIndex = z,
|
||||
.attachPoints = {
|
||||
.element = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
@@ -732,8 +732,8 @@ B32 ui_text_input(const char *id, char *buf, S32 buf_size) {
|
||||
},
|
||||
.backgroundColor = bg,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS),
|
||||
.border = { .color = border_color, .width = { 1, 1, 1, 1 } },
|
||||
.custom = { .customData = inset_grad },
|
||||
.border = { .color = border_color, .width = { 1, 1, 1, 1 } },
|
||||
) {
|
||||
if (len == 0 && !is_focused) {
|
||||
// Placeholder
|
||||
@@ -850,8 +850,8 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
},
|
||||
.backgroundColor = bg,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS),
|
||||
.border = { .color = is_open ? g_theme.accent : g_theme.border, .width = { 1, 1, 1, 1 } },
|
||||
.custom = { .customData = dd_grad },
|
||||
.border = { .color = is_open ? g_theme.accent : g_theme.border, .width = { 1, 1, 1, 1 } },
|
||||
) {
|
||||
CLAY(text_eid,
|
||||
.layout = {
|
||||
@@ -902,7 +902,6 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
},
|
||||
.backgroundColor = g_theme.bg_dark,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(CORNER_RADIUS),
|
||||
.border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } },
|
||||
.floating = {
|
||||
.parentId = eid.id,
|
||||
.zIndex = 2000,
|
||||
@@ -911,7 +910,8 @@ B32 ui_dropdown(const char *id, const char **options, S32 count, S32 *selected)
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_BOTTOM,
|
||||
},
|
||||
.attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID,
|
||||
}
|
||||
},
|
||||
.border = { .color = g_theme.border, .width = { 1, 1, 1, 1 } },
|
||||
) {
|
||||
for (S32 i = 0; i < count; i++) {
|
||||
B32 is_item_selected = (*selected == i);
|
||||
@@ -1056,8 +1056,8 @@ S32 ui_modal(const char *id, const char *title, const char *message,
|
||||
},
|
||||
.backgroundColor = g_theme.title_bar,
|
||||
.cornerRadius = { CORNER_RADIUS, CORNER_RADIUS, 0, 0 },
|
||||
.border = { .color = g_theme.border, .width = { .bottom = 1 } },
|
||||
.custom = { .customData = mtb_grad },
|
||||
.border = { .color = g_theme.border, .width = { .bottom = 1 } },
|
||||
) {
|
||||
CLAY_TEXT(clay_str(title), &g_widget_text_config);
|
||||
}
|
||||
@@ -1264,8 +1264,8 @@ B32 ui_window(const char *id, const char *title, B32 *open,
|
||||
},
|
||||
.backgroundColor = g_theme.title_bar,
|
||||
.cornerRadius = { CORNER_RADIUS, CORNER_RADIUS, 0, 0 },
|
||||
.border = { .color = g_theme.border, .width = { .bottom = 1 } },
|
||||
.custom = { .customData = tb_grad },
|
||||
.border = { .color = g_theme.border, .width = { .bottom = 1 } },
|
||||
) {
|
||||
// Title text (grows to push close button right)
|
||||
CLAY(WIDI(id, 3002),
|
||||
@@ -1916,7 +1916,6 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) },
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
.floating = {
|
||||
.offset = {
|
||||
.x = normalized * (track_w - thumb_w),
|
||||
@@ -1928,7 +1927,9 @@ B32 ui_slider_h(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
},
|
||||
.pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
}
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
) {}
|
||||
}
|
||||
}
|
||||
@@ -2092,7 +2093,6 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(thumb_w), .height = CLAY_SIZING_FIXED(thumb_h) },
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
.floating = {
|
||||
.offset = {
|
||||
.x = 0,
|
||||
@@ -2104,7 +2104,9 @@ B32 ui_slider_v(const char *id, const char *label, F32 *value, F32 max_val, B32
|
||||
},
|
||||
.pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
}
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
) {}
|
||||
}
|
||||
}
|
||||
@@ -2210,9 +2212,12 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
S32 val_len = 0;
|
||||
char *val_text = is_editing ? nullptr : value_format_text(*value, is_signed, &val_len);
|
||||
|
||||
// Dimmed accent for fill bar
|
||||
Clay_Color fill_color = g_theme.accent;
|
||||
fill_color.a = 160;
|
||||
// Tick mark dimensions
|
||||
F32 tick_major_w = WIDGET_FADER_TICK_MAJOR_W;
|
||||
F32 tick_minor_w = WIDGET_FADER_TICK_MINOR_W;
|
||||
F32 tick_h = WIDGET_FADER_TICK_H;
|
||||
S32 num_ticks = 10;
|
||||
F32 track_left = (cap_w - track_w) / 2.0f;
|
||||
|
||||
// Layout: vertical column (label → hit area with track → value/edit)
|
||||
CLAY(WID(id),
|
||||
@@ -2236,43 +2241,31 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
.childAlignment = { .x = CLAY_ALIGN_X_CENTER },
|
||||
}
|
||||
) {
|
||||
// Visible track (centered inside hit area)
|
||||
// Visible track (centered inside hit area, empty)
|
||||
CLAY(CLAY_IDI("FdrTrack", (int)hash),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(track_w), .height = CLAY_SIZING_FIXED(track_h) },
|
||||
.childAlignment = { .y = CLAY_ALIGN_Y_BOTTOM },
|
||||
},
|
||||
.backgroundColor = g_theme.bg_dark,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f)
|
||||
) {
|
||||
// Fill bar (from bottom)
|
||||
F32 fill_h = normalized * track_h;
|
||||
if (fill_h < 1.0f) fill_h = 1.0f;
|
||||
CLAY(CLAY_IDI("FdrFill", (int)hash),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_GROW(), .height = CLAY_SIZING_FIXED(fill_h) },
|
||||
},
|
||||
.backgroundColor = fill_color,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(track_w / 2.0f)
|
||||
) {}
|
||||
}
|
||||
) {}
|
||||
|
||||
// Floating fader cap (icon, silver colored)
|
||||
if (g_icon_pool_count < UI_MAX_ICONS_PER_FRAME) {
|
||||
CustomIconData *idata = &g_icon_pool[g_icon_pool_count++];
|
||||
idata->type = CUSTOM_RENDER_ICON;
|
||||
idata->icon_id = (S32)UI_ICON_FADER;
|
||||
idata->color = Clay_Color{200, 200, 210, 255};
|
||||
// Tick marks on both sides of the track
|
||||
for (S32 i = 0; i <= num_ticks; i++) {
|
||||
F32 norm = (F32)i / (F32)num_ticks;
|
||||
F32 tick_y = (1.0f - norm) * (track_h - tick_h);
|
||||
F32 tw = (i % 5 == 0) ? tick_major_w : tick_minor_w;
|
||||
|
||||
CLAY(CLAY_IDI("FdrCap", (int)hash),
|
||||
// Left tick
|
||||
CLAY(CLAY_IDI("FdrTkL", (int)(hash * 100 + i)),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) },
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) },
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
.backgroundColor = g_theme.text_dim,
|
||||
.floating = {
|
||||
.offset = {
|
||||
.x = 0,
|
||||
.y = (1.0f - normalized) * (track_h - cap_h),
|
||||
.x = track_left - tw,
|
||||
.y = tick_y,
|
||||
},
|
||||
.attachPoints = {
|
||||
.element = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
@@ -2280,7 +2273,78 @@ B32 ui_fader(const char *id, const char *label, F32 *value, F32 max_val, B32 is_
|
||||
},
|
||||
.pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
}
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
) {}
|
||||
|
||||
// Right tick
|
||||
CLAY(CLAY_IDI("FdrTkR", (int)(hash * 100 + i)),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(tw), .height = CLAY_SIZING_FIXED(tick_h) },
|
||||
},
|
||||
.backgroundColor = g_theme.text_dim,
|
||||
.floating = {
|
||||
.offset = {
|
||||
.x = track_left + track_w,
|
||||
.y = tick_y,
|
||||
},
|
||||
.attachPoints = {
|
||||
.element = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
},
|
||||
.pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
) {}
|
||||
}
|
||||
|
||||
// Floating fader cap (RGBA icon from asset SVG)
|
||||
if (g_icon_pool_count < UI_MAX_ICONS_PER_FRAME) {
|
||||
CustomIconData *idata = &g_icon_pool[g_icon_pool_count++];
|
||||
idata->type = CUSTOM_RENDER_ICON;
|
||||
idata->icon_id = (S32)UI_ICON_FADER;
|
||||
idata->color = Clay_Color{255, 255, 255, 255};
|
||||
|
||||
F32 cap_y = (1.0f - normalized) * (track_h - cap_h);
|
||||
|
||||
CLAY(CLAY_IDI("FdrCap", (int)hash),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) },
|
||||
},
|
||||
.floating = {
|
||||
.offset = { .x = 0, .y = cap_y },
|
||||
.attachPoints = {
|
||||
.element = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
},
|
||||
.pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
.custom = { .customData = idata },
|
||||
) {}
|
||||
|
||||
// Color tint overlay on top of fader cap
|
||||
Clay_Color tint = g_theme.accent;
|
||||
tint.a = 80;
|
||||
F32 cap_corner = cap_w * (3.0f / 62.2f);
|
||||
CLAY(CLAY_IDI("FdrTint", (int)hash),
|
||||
.layout = {
|
||||
.sizing = { .width = CLAY_SIZING_FIXED(cap_w), .height = CLAY_SIZING_FIXED(cap_h) },
|
||||
},
|
||||
.backgroundColor = tint,
|
||||
.cornerRadius = CLAY_CORNER_RADIUS(cap_corner),
|
||||
.floating = {
|
||||
.offset = { .x = 0, .y = cap_y },
|
||||
.attachPoints = {
|
||||
.element = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
.parent = CLAY_ATTACH_POINT_LEFT_TOP,
|
||||
},
|
||||
.pointerCaptureMode = CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
|
||||
.attachTo = CLAY_ATTACH_TO_PARENT,
|
||||
.clipTo = CLAY_CLIP_TO_ATTACHED_PARENT,
|
||||
},
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user