Rebrand to Galaxy, major improvements to Bedrock support, still needs some TLC though

This commit is contained in:
Ryan Ward
2026-05-07 11:29:34 -05:00
parent f4e2475c60
commit a41cbd8cc7
2433 changed files with 14208 additions and 9409 deletions
@@ -0,0 +1,35 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
pub static ASSETS: Assets = Assets;
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {path}"))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(|ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,84 @@
use galaxyui::color::ColorU;
use galaxyui::elements::shimmering_text::{
ShimmerConfig, ShimmeringTextElement, ShimmeringTextStateHandle,
};
use galaxyui::elements::{Align, ConstrainedBox, ParentElement, Rect, Stack};
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{AppContext, Element, Entity, TypedActionView, View, ViewContext};
pub struct RootView {
text: String,
font_family: FamilyId,
font_size: f32,
start: ColorU,
end: ColorU,
config: ShimmerConfig,
// Persist the animation/layout state across renders.
shimmering_text_handle: ShimmeringTextStateHandle,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let font_family = galaxyui::fonts::Cache::handle(ctx).update(ctx, |cache, _| {
cache
.load_system_font("Times")
.or_else(|_| cache.load_system_font("Arial"))
.expect("Should load a system font")
});
// Treat start/end as the dim → bright endpoints.
let start = ColorU::new(160, 160, 160, 255);
let end = ColorU::new(255, 255, 255, 255);
Self {
text: "Warp shimmer: 👩‍💻with ligatures — fi fl 🇺🇸".to_string(),
font_family,
font_size: 28.0,
start,
end,
config: ShimmerConfig::default(),
shimmering_text_handle: ShimmeringTextStateHandle::new(),
}
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"AnimatedGradientText"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
let shimmering = ShimmeringTextElement::new(
self.text.clone(),
self.font_family,
self.font_size,
self.start,
self.end,
self.config,
self.shimmering_text_handle.clone(),
)
.finish();
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(
Align::new(
ConstrainedBox::new(shimmering)
.with_max_width(900.)
.finish(),
)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,34 @@
use anyhow::{anyhow, Result};
use root_view::RootView;
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(galaxyui::AddWindowOptions::default(), |_| RootView::new());
});
Ok(())
}
@@ -0,0 +1,99 @@
use instant::Instant;
use galaxyui::{
assets::asset_cache::AssetSource,
elements::{
CacheOption, ConstrainedBox, CrossAxisAlignment, Flex, Image, ParentElement, Shrinkable,
Stack,
},
AppContext, Element, Entity, TypedActionView, View,
};
pub struct RootView {
animation_start_time: Instant,
}
impl RootView {
pub fn new() -> Self {
println!(
"WARN: This example is slow to start up due to the huge GIF. Compiling with --release \
helps."
);
RootView {
animation_start_time: Instant::now(),
}
}
}
impl Default for RootView {
fn default() -> Self {
Self::new()
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Stack::new()
.with_child(
Shrinkable::new(
1.,
Image::new(
AssetSource::Bundled {
path: "rustyrain.gif",
},
CacheOption::Original,
)
.enable_animation_with_start_time(self.animation_start_time)
.cover()
.finish(),
)
.finish(),
)
.with_child(
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_child(
ConstrainedBox::new(
Image::new(
AssetSource::Bundled {
path: "numbers-1000ms.gif",
},
CacheOption::BySize,
)
.enable_animation_with_start_time(self.animation_start_time)
.finish(),
)
.with_height(350.)
.with_width(350.)
.finish(),
)
.with_child(
ConstrainedBox::new(
Image::new(
AssetSource::Bundled {
path: "numbers-750ms.gif",
},
CacheOption::BySize,
)
.enable_animation_with_start_time(self.animation_start_time)
.finish(),
)
.with_height(350.)
.with_width(350.)
.finish(),
)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1 @@
This directory is used for any static assets we want to load in the app (e.g. svg icons, images, fonts, etc.)
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.477 4.0729C2.806 4.2489 2.238 4.8229 2.06 5.5079C1.941 5.9619 2.003 6.4919 2.225 6.9299C2.361 7.1989 2.801 7.6389 3.07 7.7749C3.659 8.0729 4.341 8.0729 4.93 7.7749C5.196 7.6399 5.638 7.1999 5.771 6.9369C5.994 6.4979 6.052 6.0069 5.94 5.5289C5.858 5.1819 5.72 4.9269 5.465 4.6529C4.948 4.0969 4.211 3.8819 3.477 4.0729ZM8.614 5.0569C8.377 5.1309 8.212 5.2609 8.109 5.4529C8.035 5.5919 8.02 5.6839 8.02 5.9999C8.02 6.3309 8.033 6.4029 8.12 6.5579C8.23 6.7549 8.414 6.8979 8.635 6.9589C8.834 7.0149 21.166 7.0149 21.365 6.9589C21.586 6.8979 21.77 6.7549 21.88 6.5579C21.967 6.4019 21.98 6.3319 21.979 5.9999C21.978 5.5669 21.898 5.3549 21.678 5.1989C21.388 4.9919 21.642 4.9999 14.984 5.0029C10.009 5.0049 8.747 5.0159 8.614 5.0569ZM3.477 10.0729C3.13 10.1639 2.802 10.3659 2.535 10.6529C2.178 11.0379 2 11.4849 2 11.9999C2 12.5459 2.193 13.0009 2.596 13.4039C2.741 13.5499 2.955 13.7159 3.07 13.7749C3.659 14.0729 4.341 14.0729 4.93 13.7749C5.196 13.6399 5.638 13.1999 5.771 12.9369C6.161 12.1679 6.043 11.2749 5.471 10.6589C5.074 10.2319 4.563 10.0049 4 10.0049C3.857 10.0049 3.622 10.0359 3.477 10.0729ZM8.614 11.0569C8.377 11.1309 8.212 11.2609 8.109 11.4529C8.035 11.5919 8.02 11.6839 8.02 11.9999C8.02 12.3309 8.033 12.4029 8.12 12.5579C8.23 12.7549 8.414 12.8979 8.635 12.9589C8.834 13.0149 21.166 13.0149 21.365 12.9589C21.586 12.8979 21.77 12.7549 21.88 12.5579C21.967 12.4019 21.98 12.3319 21.979 11.9999C21.978 11.5669 21.898 11.3549 21.678 11.1989C21.388 10.9919 21.642 10.9999 14.984 11.0029C10.009 11.0049 8.747 11.0159 8.614 11.0569ZM3.477 16.0729C3.13 16.1639 2.802 16.3659 2.535 16.6529C2.178 17.0379 2 17.4849 2 17.9999C2 18.5459 2.193 19.0009 2.596 19.4039C2.999 19.8069 3.454 19.9999 4 19.9999C4.546 19.9999 5.001 19.8069 5.404 19.4039C5.92 18.8889 6.106 18.2359 5.94 17.5289C5.858 17.1819 5.72 16.9269 5.465 16.6529C5.074 16.2319 4.56 16.0049 4 16.0049C3.857 16.0049 3.622 16.0359 3.477 16.0729ZM8.614 17.0569C8.377 17.1309 8.212 17.2609 8.109 17.4529C8.035 17.5919 8.02 17.6839 8.02 17.9999C8.02 18.3309 8.033 18.4029 8.12 18.5579C8.23 18.7549 8.414 18.8979 8.635 18.9589C8.834 19.0149 21.166 19.0149 21.365 18.9589C21.586 18.8979 21.77 18.7549 21.88 18.5579C21.967 18.4019 21.98 18.3319 21.979 17.9999C21.978 17.5669 21.898 17.3549 21.678 17.1989C21.388 16.9919 21.642 16.9999 14.984 17.0029C10.009 17.0049 8.747 17.0159 8.614 17.0569Z" fill="#FF0000"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.536 1.03894C10.781 1.14594 9.98205 1.60094 9.51205 2.19394C9.25205 2.52094 9.15305 2.70094 8.80005 3.49994C8.34005 4.53894 8.19505 4.73894 7.76005 4.93494C7.42605 5.08594 7.25305 5.09394 6.40005 4.99894C5.14705 4.85894 4.63305 4.89094 4.03005 5.14394C3.08205 5.54294 2.42005 6.32094 2.12705 7.37994C2.03405 7.71594 2.04005 8.52794 2.13705 8.87994C2.26405 9.33694 2.53705 9.83294 3.04105 10.5199C3.72605 11.4549 3.74005 11.4839 3.74005 11.9999C3.74005 12.5149 3.72705 12.5429 3.04305 13.4799C2.24405 14.5739 2.06705 15.0069 2.06305 15.8799C2.06005 16.5789 2.20005 17.0639 2.56705 17.6279C3.02105 18.3239 3.75005 18.8409 4.52705 19.0169C5.02104 19.1289 5.26505 19.1289 6.26105 19.0219C7.30105 18.9099 7.48005 18.9169 7.80305 19.0819C7.89405 19.1279 8.05005 19.2409 8.15105 19.3329C8.33905 19.5049 8.45505 19.7219 8.96305 20.8579C9.31305 21.6389 9.66205 22.0809 10.227 22.4529C11.424 23.2419 12.902 23.1679 14.049 22.2619C14.513 21.8949 14.801 21.4259 15.402 20.0599C15.659 19.4769 15.913 19.1879 16.298 19.0439C16.602 18.9299 16.831 18.9299 17.88 19.0409C18.625 19.1209 18.789 19.1269 19.105 19.0869C21.04 18.8449 22.307 17.0199 21.855 15.1259C21.754 14.6989 21.474 14.1879 20.957 13.4799C20.28 12.5539 20.26 12.5129 20.26 12.0199C20.26 11.5269 20.313 11.4069 20.846 10.6799C21.775 9.41494 21.931 9.04894 21.936 8.11994C21.939 7.59094 21.893 7.34194 21.704 6.87394C21.388 6.09294 20.705 5.43194 19.88 5.10894C19.304 4.88394 18.833 4.86094 17.6 4.99894C17.169 5.04694 16.739 5.07394 16.64 5.06094C16.357 5.02194 15.988 4.82194 15.788 4.60094C15.671 4.47094 15.54 4.24494 15.4 3.93194C14.768 2.51594 14.545 2.14894 14.1 1.78894C13.667 1.43794 13.219 1.20794 12.752 1.09594C12.452 1.02494 11.842 0.995943 11.536 1.03894ZM12.346 3.05994C12.598 3.13394 12.848 3.31294 12.98 3.51294C13.036 3.59694 13.243 4.02294 13.44 4.45994C13.87 5.41094 13.982 5.60494 14.316 5.96694C14.904 6.60494 15.691 6.98494 16.583 7.05994C16.803 7.07894 17.144 7.05994 17.7 6.99894C18.704 6.88894 18.963 6.88894 19.209 6.99494C19.47 7.10794 19.733 7.36294 19.844 7.61094C19.957 7.86394 19.97 8.33394 19.869 8.56094C19.835 8.63794 19.571 9.02494 19.282 9.42094C18.461 10.5469 18.236 11.1019 18.237 11.9969C18.237 12.4909 18.305 12.8679 18.463 13.2639C18.614 13.6409 18.719 13.8099 19.282 14.5819C19.869 15.3889 19.935 15.5219 19.935 15.8999C19.935 16.4919 19.553 16.9559 18.961 17.0829C18.801 17.1179 18.6 17.1089 17.97 17.0399C16.523 16.8819 16.051 16.9319 15.27 17.3249C14.938 17.4919 14.705 17.6539 14.445 17.8979C14.037 18.2819 13.855 18.5949 13.237 19.9799C13.076 20.3399 12.976 20.5059 12.839 20.6439C12.423 21.0609 11.77 21.1099 11.287 20.7609C11.061 20.5969 10.969 20.4459 10.618 19.6589C10.088 18.4729 9.97005 18.2819 9.49005 17.8369C8.95805 17.3449 8.26305 17.0339 7.52405 16.9589C7.19905 16.9249 6.99105 16.9339 6.19505 17.0179C5.67705 17.0719 5.18205 17.1059 5.09605 17.0929C4.69405 17.0319 4.31704 16.7419 4.14804 16.3649C4.04104 16.1249 4.03404 15.7279 4.13205 15.4739C4.17204 15.3709 4.43705 14.9659 4.72305 14.5739C5.56105 13.4209 5.76604 12.9159 5.76604 11.9999C5.76604 11.0839 5.56105 10.5789 4.72305 9.42594C4.43705 9.03394 4.17204 8.62894 4.13205 8.52594C3.91205 7.95594 4.18705 7.28294 4.74905 7.01694C5.03005 6.88494 5.23105 6.88194 6.30005 6.99894C7.18105 7.09594 7.52205 7.08694 8.03705 6.95594C8.95404 6.72194 9.78204 6.06894 10.197 5.25294C10.262 5.12494 10.451 4.71494 10.618 4.34094C10.961 3.57394 11.056 3.41294 11.262 3.25594C11.568 3.02194 11.974 2.94894 12.346 3.05994ZM11.358 8.06094C10.23 8.23694 9.14305 8.97394 8.56905 9.95294C7.32005 12.0829 8.23304 14.7849 10.512 15.7039C12.558 16.5279 14.877 15.5379 15.704 13.4879C16.09 12.5299 16.09 11.4689 15.705 10.5159C15.172 9.19694 14.001 8.26894 12.605 8.05794C12.144 7.98894 11.816 7.98994 11.358 8.06094ZM12.523 10.0729C13.262 10.2659 13.85 10.9239 13.967 11.6899C14.067 12.3429 13.873 12.9329 13.403 13.4039C13.002 13.8069 12.547 13.9999 12 13.9999C11.485 13.9999 11.038 13.8219 10.653 13.4649C9.32304 12.2289 10.189 10.0049 12 10.0049C12.143 10.0049 12.378 10.0359 12.523 10.0729Z" fill="#FF0000"/>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.33334 2.66659C4.75601 2.66659 2.66668 4.75592 2.66668 7.33325C2.66668 9.91058 4.75601 11.9999 7.33334 11.9999C9.91067 11.9999 12 9.91058 12 7.33325C12 4.75592 9.91067 2.66659 7.33334 2.66659ZM1.33334 7.33325C1.33334 4.01954 4.01963 1.33325 7.33334 1.33325C10.6471 1.33325 13.3333 4.01954 13.3333 7.33325C13.3333 10.647 10.6471 13.3333 7.33334 13.3333C4.01963 13.3333 1.33334 10.647 1.33334 7.33325Z" fill="#ffffff"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6286 10.6286C10.8889 10.3683 11.311 10.3683 11.5714 10.6286L14.4714 13.5286C14.7317 13.789 14.7317 14.2111 14.4714 14.4714C14.211 14.7318 13.7889 14.7318 13.5286 14.4714L10.6286 11.5714C10.3682 11.3111 10.3682 10.889 10.6286 10.6286Z" fill="#ffffff"/>
</svg>

After

Width:  |  Height:  |  Size: 885 B

@@ -0,0 +1,6 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.5774 0.364039C5.40473 0.486706 4.3334 1.22337 3.75073 2.30871C3.28207 3.18137 3.2114 4.3047 3.56407 5.27137C3.93607 6.29137 4.79873 7.12537 5.84407 7.47537C7.1234 7.90404 8.54807 7.58537 9.5174 6.65404C10.1961 6.00204 10.5781 5.20604 10.6507 4.28937C10.7387 3.18204 10.3401 2.13871 9.53073 1.35937C8.7414 0.598706 7.69673 0.246706 6.5774 0.364039ZM7.60073 1.74471C8.22407 1.90537 8.78207 2.36337 9.08073 2.96004C9.27607 3.35004 9.3074 3.49137 9.3074 4.00004C9.3074 4.50871 9.27607 4.65071 9.08073 5.04004C8.78673 5.62671 8.28273 6.05137 7.65407 6.24271C7.43673 6.30937 7.3754 6.31604 7.00073 6.31604C6.62673 6.31604 6.56473 6.30937 6.3474 6.24337C5.56607 6.00604 4.9574 5.38271 4.7414 4.59804C4.7054 4.46737 4.69407 4.32604 4.69407 4.00004C4.69407 3.53871 4.72207 3.38071 4.86007 3.06871C5.11673 2.48604 5.66807 1.97804 6.25007 1.78737C6.6514 1.65604 7.1894 1.63871 7.60073 1.74471ZM6.33407 9.02804C4.2194 9.21204 2.33607 10.088 0.786734 11.6094C0.386734 12.002 0.302734 12.13 0.302734 12.348C0.302734 12.6614 0.687401 13.0314 1.01273 13.0314C1.19407 13.0314 1.33407 12.9387 1.72073 12.5627C2.9614 11.356 4.33073 10.6627 5.97407 10.4087C6.2814 10.3607 6.4634 10.3507 7.00073 10.3507C7.53807 10.3507 7.72007 10.3607 8.0274 10.4087C9.67473 10.6634 11.0294 11.3507 12.2941 12.574C12.6907 12.9574 12.8021 13.0314 12.9874 13.0314C13.3141 13.0314 13.6987 12.662 13.6987 12.348C13.6987 12.1 13.5527 11.9054 12.9607 11.3654C11.5514 10.0794 9.8454 9.28537 8.01407 9.0647C7.6014 9.0147 6.70807 8.99537 6.33407 9.02804Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.5774 0.364039C5.40473 0.486706 4.3334 1.22337 3.75073 2.30871C3.28207 3.18137 3.2114 4.3047 3.56407 5.27137C3.93607 6.29137 4.79873 7.12537 5.84407 7.47537C7.1234 7.90404 8.54807 7.58537 9.5174 6.65404C10.1961 6.00204 10.5781 5.20604 10.6507 4.28937C10.7387 3.18204 10.3401 2.13871 9.53073 1.35937C8.7414 0.598706 7.69673 0.246706 6.5774 0.364039ZM7.60073 1.74471C8.22407 1.90537 8.78207 2.36337 9.08073 2.96004C9.27607 3.35004 9.3074 3.49137 9.3074 4.00004C9.3074 4.50871 9.27607 4.65071 9.08073 5.04004C8.78673 5.62671 8.28273 6.05137 7.65407 6.24271C7.43673 6.30937 7.3754 6.31604 7.00073 6.31604C6.62673 6.31604 6.56473 6.30937 6.3474 6.24337C5.56607 6.00604 4.9574 5.38271 4.7414 4.59804C4.7054 4.46737 4.69407 4.32604 4.69407 4.00004C4.69407 3.53871 4.72207 3.38071 4.86007 3.06871C5.11673 2.48604 5.66807 1.97804 6.25007 1.78737C6.6514 1.65604 7.1894 1.63871 7.60073 1.74471ZM6.33407 9.02804C4.2194 9.21204 2.33607 10.088 0.786734 11.6094C0.386734 12.002 0.302734 12.13 0.302734 12.348C0.302734 12.6614 0.687401 13.0314 1.01273 13.0314C1.19407 13.0314 1.33407 12.9387 1.72073 12.5627C2.9614 11.356 4.33073 10.6627 5.97407 10.4087C6.2814 10.3607 6.4634 10.3507 7.00073 10.3507C7.53807 10.3507 7.72007 10.3607 8.0274 10.4087C9.67473 10.6634 11.0294 11.3507 12.2941 12.574C12.6907 12.9574 12.8021 13.0314 12.9874 13.0314C13.3141 13.0314 13.6987 12.662 13.6987 12.348C13.6987 12.1 13.5527 11.9054 12.9607 11.3654C11.5514 10.0794 9.8454 9.28537 8.01407 9.0647C7.6014 9.0147 6.70807 8.99537 6.33407 9.02804Z" fill="white" fill-opacity="0.6"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.5774 0.364039C5.40473 0.486706 4.3334 1.22337 3.75073 2.30871C3.28207 3.18137 3.2114 4.3047 3.56407 5.27137C3.93607 6.29137 4.79873 7.12537 5.84407 7.47537C7.1234 7.90404 8.54807 7.58537 9.5174 6.65404C10.1961 6.00204 10.5781 5.20604 10.6507 4.28937C10.7387 3.18204 10.3401 2.13871 9.53073 1.35937C8.7414 0.598706 7.69673 0.246706 6.5774 0.364039ZM7.60073 1.74471C8.22407 1.90537 8.78207 2.36337 9.08073 2.96004C9.27607 3.35004 9.3074 3.49137 9.3074 4.00004C9.3074 4.50871 9.27607 4.65071 9.08073 5.04004C8.78673 5.62671 8.28273 6.05137 7.65407 6.24271C7.43673 6.30937 7.3754 6.31604 7.00073 6.31604C6.62673 6.31604 6.56473 6.30937 6.3474 6.24337C5.56607 6.00604 4.9574 5.38271 4.7414 4.59804C4.7054 4.46737 4.69407 4.32604 4.69407 4.00004C4.69407 3.53871 4.72207 3.38071 4.86007 3.06871C5.11673 2.48604 5.66807 1.97804 6.25007 1.78737C6.6514 1.65604 7.1894 1.63871 7.60073 1.74471ZM6.33407 9.02804C4.2194 9.21204 2.33607 10.088 0.786734 11.6094C0.386734 12.002 0.302734 12.13 0.302734 12.348C0.302734 12.6614 0.687401 13.0314 1.01273 13.0314C1.19407 13.0314 1.33407 12.9387 1.72073 12.5627C2.9614 11.356 4.33073 10.6627 5.97407 10.4087C6.2814 10.3607 6.4634 10.3507 7.00073 10.3507C7.53807 10.3507 7.72007 10.3607 8.0274 10.4087C9.67473 10.6634 11.0294 11.3507 12.2941 12.574C12.6907 12.9574 12.8021 13.0314 12.9874 13.0314C13.3141 13.0314 13.6987 12.662 13.6987 12.348C13.6987 12.1 13.5527 11.9054 12.9607 11.3654C11.5514 10.0794 9.8454 9.28537 8.01407 9.0647C7.6014 9.0147 6.70807 8.99537 6.33407 9.02804Z" fill="black" fill-opacity="0.2"/>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 MiB

@@ -0,0 +1,37 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
root_view::init(ctx);
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,245 @@
use pathfinder_color::ColorU;
use galaxyui::elements::DispatchEventResult;
use galaxyui::fonts::FamilyId;
use galaxyui::{
elements::{
Align, Border, ChildView, Container, CornerRadius, EventHandler, Flex, ParentElement,
Radius, Rect, Stack, Text,
},
AppContext, Element, Entity, ModelHandle, SingletonEntity, Tracked, TypedActionView, View,
ViewContext, ViewHandle,
};
pub fn init(ctx: &mut AppContext) {
ctx.add_singleton_model(|_| Settings {
dark_mode: Tracked::new(false),
});
}
pub struct RootView {
main: ViewHandle<MainView>,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let main = ctx.add_typed_action_view(MainView::new);
RootView { main }
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, app: &AppContext) -> Box<dyn Element> {
let dark_mode = *Settings::as_ref(app).dark_mode;
Stack::new()
.with_child(
Rect::new()
.with_background_color(if dark_mode {
ColorU::black()
} else {
ColorU::white()
})
.finish(),
)
.with_child(ChildView::new(&self.main).finish())
.finish()
}
}
struct Settings {
dark_mode: Tracked<bool>,
}
impl Entity for Settings {
type Event = ();
}
impl SingletonEntity for Settings {}
#[derive(Default)]
struct Counter {
value: Tracked<isize>,
}
impl Counter {
fn increment(&mut self) {
*self.value += 1;
}
fn decrement(&mut self) {
*self.value -= 1;
}
fn value(&self) -> isize {
*self.value
}
}
impl Entity for Counter {
type Event = ();
}
struct MainView {
model: ModelHandle<Counter>,
stored: Tracked<Option<isize>>,
font_family: FamilyId,
}
#[derive(Clone, Copy, Debug)]
enum MainViewAction {
Increment,
Decrement,
Save,
Restore,
ToggleDarkMode,
}
impl MainView {
fn new(ctx: &mut ViewContext<Self>) -> Self {
let model = ctx.add_model(|_| Counter::default());
let font_family = galaxyui::fonts::Cache::handle(ctx)
.update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
MainView {
model,
stored: Tracked::new(None),
font_family,
}
}
}
impl Entity for MainView {
type Event = ();
}
impl TypedActionView for MainView {
type Action = MainViewAction;
fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
use MainViewAction::*;
match action {
Increment => self.model.update(ctx, |model, _| model.increment()),
Decrement => self.model.update(ctx, |model, _| model.decrement()),
Save => {
let current = self.model.read(ctx, |model, _| model.value());
*self.stored = Some(current);
}
Restore => {
if let Some(stored) = self.stored.take() {
self.model.update(ctx, |model, _| *model.value = stored);
}
}
ToggleDarkMode => {
Settings::handle(ctx).update(ctx, |settings, _| {
*settings.dark_mode = !*settings.dark_mode;
});
}
}
}
}
impl View for MainView {
fn ui_name() -> &'static str {
"MainView"
}
fn render(&self, app: &AppContext) -> Box<dyn Element> {
let dark_mode = *Settings::as_ref(app).dark_mode;
let text_color = if dark_mode {
ColorU::white()
} else {
ColorU::black()
};
let counter = self.model.as_ref(app).value();
Align::new(
Flex::column()
.with_child(
Align::new(
Flex::row()
.with_child(render_button(
Text::new_inline("-", self.font_family, 16.)
.with_color(text_color)
.finish(),
MainViewAction::Decrement,
))
.with_child(
Text::new_inline(format!("{counter}"), self.font_family, 16.)
.with_color(text_color)
.finish(),
)
.with_child(render_button(
Text::new_inline("+", self.font_family, 16.)
.with_color(text_color)
.finish(),
MainViewAction::Increment,
))
.finish(),
)
.finish(),
)
.with_child(
Align::new(
Flex::row()
.with_child(render_button(
Text::new_inline("Toggle Dark Mode", self.font_family, 16.)
.with_color(text_color)
.finish(),
MainViewAction::ToggleDarkMode,
))
.with_child(if self.stored.is_some() {
render_button(
Text::new_inline("Restore", self.font_family, 16.)
.with_color(text_color)
.finish(),
MainViewAction::Restore,
)
} else {
render_button(
Text::new_inline("Save Value", self.font_family, 16.)
.with_color(text_color)
.finish(),
MainViewAction::Save,
)
})
.finish(),
)
.finish(),
)
.finish(),
)
.finish()
}
}
fn render_button(inner: Box<dyn Element>, action: MainViewAction) -> Box<dyn Element> {
Container::new(
EventHandler::new(
Container::new(inner)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(4.)))
.with_border(Border::all(1.).with_border_color(ColorU::new(128, 128, 128, 255)))
.with_uniform_padding(4.)
.finish(),
)
.on_left_mouse_down(move |ctx, _, _| {
ctx.dispatch_typed_action(action);
DispatchEventResult::StopPropagation
})
.finish(),
)
.with_uniform_margin(4.)
.finish()
}
impl TypedActionView for RootView {
type Action = ();
}
+40
View File
@@ -0,0 +1,40 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions {
// Set the window background blur radius to 18 pixels.
background_blur_radius_pixels: Some(18),
..Default::default()
},
|_| root_view::BlurredView {},
);
});
Ok(())
}
@@ -0,0 +1,25 @@
use pathfinder_color::ColorU;
use galaxyui::{elements::Rect, AppContext, Element, Entity, TypedActionView, View};
pub struct BlurredView {}
impl Entity for BlurredView {
type Event = ();
}
impl View for BlurredView {
fn ui_name() -> &'static str {
"RootView"
}
/// Renders a transparent red rectangle. The blur effect is applied on the window (see
/// `open_new()`.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Rect::new()
.with_background_color(ColorU::new(255, 0, 0, 50))
.finish()
}
}
impl TypedActionView for BlurredView {
type Action = ();
}
@@ -0,0 +1,37 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
#[allow(unused_mut)]
let mut app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(galaxyui::AddWindowOptions::default(), |_| {
root_view::RootView::default()
});
});
Ok(())
}
@@ -0,0 +1,186 @@
use std::any::Any;
use pathfinder_color::ColorU;
use pathfinder_geometry::{
rect::RectF,
vector::{vec2f, Vector2F},
};
use galaxyui::elements::{AcceptedByDropTarget, DropTarget, DropTargetData};
use galaxyui::{
elements::{
Align, ConstrainedBox, Container, DragAxis, Draggable, DraggableState, ParentElement, Rect,
Stack,
},
AppContext, Element, Entity, TypedActionView, View,
};
#[derive(Default)]
pub struct RootView {
basic_draggable_state: DraggableState,
horizontal_draggable_state: DraggableState,
vertical_draggable_state: DraggableState,
clamped_draggable_state: DraggableState,
}
// Implement the entity trait.
impl Entity for RootView {
type Event = ();
}
// Implement the view trait so RootView could be considered as a view.
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
// Let's render a simple black rect background.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(
Align::new(
Draggable::new(
self.basic_draggable_state.clone(),
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.with_width(50.)
.with_height(50.)
.finish(),
)
.on_drag_start(|_, _, _| eprintln!("Regular Drag Start!"))
.on_drop(|_, _, _, drop_data| eprintln!("Regular Drop! Data: {drop_data:?}"))
.with_accepted_by_drop_target_fn(|_, _| AcceptedByDropTarget::Yes)
.with_drag_bounds_callback(|_, window_size| {
Some(RectF::new(Vector2F::zero(), window_size))
})
.finish(),
)
.finish(),
)
.with_child(
Align::new(
Container::new({
let draggable = Draggable::new(
self.horizontal_draggable_state.clone(),
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(255, 0, 255, 255))
.finish(),
)
.with_width(100.)
.with_height(50.)
.finish(),
)
.with_drag_axis(DragAxis::HorizontalOnly)
.on_drag_start(|_, _, _| eprintln!("Horizontal Drag Start!"))
.on_drop(|_, _, _, drop_data| {
eprintln!("Horizontal Drop! Drop data: {drop_data:?}")
})
.with_accepted_by_drop_target_fn(|_, _| AcceptedByDropTarget::Yes)
.finish();
DropTarget::new(draggable, DropTargetColor::Magenta).finish()
})
.with_margin_top(20.)
.finish(),
)
.top_center()
.finish(),
)
.with_child(
Align::new(
Container::new(
Draggable::new(
self.vertical_draggable_state.clone(),
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(0, 255, 255, 255))
.finish(),
)
.with_width(50.)
.with_height(100.)
.finish(),
)
.with_drag_axis(DragAxis::VerticalOnly)
.on_drag_start(|_, _, _| eprintln!("Vertical Drag Start!"))
.on_drop(|_, _, _, _| eprintln!("Vertical Drop!"))
.with_accepted_by_drop_target_fn(|_, _| AcceptedByDropTarget::Yes)
.finish(),
)
.with_margin_right(20.)
.finish(),
)
.right()
.finish(),
)
.with_child(
Align::new(
Container::new({
let draggable = Draggable::new(
self.clamped_draggable_state.clone(),
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(255, 255, 0, 255))
.finish(),
)
.with_width(50.)
.with_height(50.)
.finish(),
)
.with_drag_bounds(RectF::new(vec2f(20., 30.), vec2f(200., 400.)))
.on_drag_start(|_, _, _| eprintln!("Clamped Drag Start!"))
.on_drop(|_, _, _, drop_target_data| {
eprintln!("Clamped Drop! Data: {drop_target_data:?}")
})
.with_accepted_by_drop_target_fn(|_, _| AcceptedByDropTarget::Yes)
.finish();
DropTarget::new(draggable, DropTargetColor::Yellow).finish()
})
.with_margin_left(50.)
.with_margin_top(50.)
.finish(),
)
.top_left()
.finish(),
)
.with_child(
Align::new(
DropTarget::new(
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.with_width(100.)
.with_height(100.)
.finish(),
DropTargetColor::Blue,
)
.finish(),
)
.bottom_center()
.finish(),
)
.finish()
}
}
#[derive(Debug)]
enum DropTargetColor {
Yellow,
Blue,
Magenta,
}
impl DropTargetData for DropTargetColor {
fn as_any(&self) -> &dyn Any {
self
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,35 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(galaxyui::AddWindowOptions::default(), |_| {
root_view::RootView {}
});
});
Ok(())
}
@@ -0,0 +1,25 @@
use pathfinder_color::ColorU;
use galaxyui::{elements::Rect, AppContext, Element, Entity, TypedActionView, View};
pub struct RootView {}
// Implement the entity trait.
impl Entity for RootView {
type Event = ();
}
// Implement the view trait so RootView could be considered as a view.
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
// Let's render a simple black rect background.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Rect::new().with_background_color(ColorU::black()).finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,352 @@
use galaxyui::elements::{Expanded, Shrinkable};
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
elements::{
Border, ConstrainedBox, Container, Flex, MainAxisAlignment, MainAxisSize, ParentElement,
Rect, Stack, Text,
},
AppContext, Element, Entity, TypedActionView, View, ViewContext,
};
use galaxyui::color::ColorU;
pub struct RootView {
font_family: FamilyId,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let font_family = galaxyui::fonts::Cache::handle(ctx)
.update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
RootView { font_family }
}
fn make_label(&self, label: String) -> Box<dyn Element> {
Flex::row()
.with_child(Text::new_inline(label, self.font_family, 16.).finish())
.finish()
}
fn make_expanded_row(&self) -> Flex {
Flex::row()
.with_child(
Container::new(
ConstrainedBox::new(
Text::new_inline("Fixed 100", self.font_family, 16.).finish(),
)
.with_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.with_child(
Expanded::new(
1.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Max Width 500, Min 200", self.font_family, 16.)
.finish(),
)
.with_max_width(500.)
.with_min_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 150, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(
Text::new_inline("Fixed 100", self.font_family, 16.).finish(),
)
.with_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.with_main_axis_size(MainAxisSize::Max)
}
fn make_shrinkable_row(&self) -> Flex {
Flex::row()
.with_child(
Container::new(
ConstrainedBox::new(
Text::new_inline("Fixed 100", self.font_family, 16.).finish(),
)
.with_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.with_child(
Shrinkable::new(
1.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Max Width 500, Min 200", self.font_family, 16.)
.finish(),
)
.with_max_width(500.)
.with_min_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 150, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(
Text::new_inline("Fixed 100", self.font_family, 16.).finish(),
)
.with_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.with_main_axis_size(MainAxisSize::Max)
}
fn make_multiple_expanded_row(&self) -> Flex {
Flex::row()
.with_child(
Expanded::new(
3.0,
Container::new(Text::new_inline("Flex: 3.0", self.font_family, 16.).finish())
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Expanded::new(
1.0,
Container::new(Text::new_inline("Flex: 1.0", self.font_family, 16.).finish())
.with_background_color(ColorU::new(0, 150, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Expanded::new(
2.0,
Container::new(Text::new_inline("Flex: 2.0", self.font_family, 16.).finish())
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.finish(),
)
.with_main_axis_size(MainAxisSize::Max)
}
fn make_multiple_expanded_with_constraints_row(&self) -> Flex {
Flex::row()
.with_child(
Expanded::new(
1.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Min 100, Max 200", self.font_family, 16.).finish(),
)
.with_max_width(200.)
.with_min_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Expanded::new(
1.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Min 100, Max 200", self.font_family, 16.).finish(),
)
.with_max_width(200.)
.with_min_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 150, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Expanded::new(
1.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Min 100, Max 200", self.font_family, 16.).finish(),
)
.with_max_width(200.)
.with_min_width(100.)
.finish(),
)
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.finish(),
)
.with_main_axis_size(MainAxisSize::Min)
}
fn make_multiple_expanded_with_constraints_varying_flex_row(&self) -> Flex {
Flex::row()
.with_child(
Expanded::new(
3.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Min 100, Max 200, Flex: 3.0", self.font_family, 16.)
.finish(),
)
.with_max_width(200.)
.with_min_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Expanded::new(
1.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Min 100, Max 200, Flex: 1.0", self.font_family, 16.)
.finish(),
)
.with_max_width(200.)
.with_min_width(100.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 150, 0, 255))
.finish(),
)
.finish(),
)
.with_child(
Expanded::new(
2.0,
Container::new(
ConstrainedBox::new(
Text::new_inline("Min 100, Max 200, Flex: 2.0", self.font_family, 16.)
.finish(),
)
.with_max_width(200.)
.with_min_width(100.)
.finish(),
)
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.finish(),
)
.with_main_axis_size(MainAxisSize::Min)
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
// Let's render a simple black rect background.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
let row_expanded = Container::new(
self.make_expanded_row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_shrinkable = Container::new(
self.make_shrinkable_row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.with_margin_bottom(32.)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let multiple_expanded_row = Container::new(
self.make_multiple_expanded_row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let multiple_expanded_with_constraints_row = Container::new(
self.make_multiple_expanded_with_constraints_row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let multiple_expanded_with_constraints_varying_flex_row = Container::new(
self.make_multiple_expanded_with_constraints_varying_flex_row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(
Container::new(
Flex::column()
.with_child(self.make_label("Expanded - FlexFit::Tight".to_owned()))
.with_child(row_expanded)
.with_child(
self.make_label(
"Shrinkable (Old Expanded) - FlexFit::Loose ".to_owned(),
),
)
.with_child(row_shrinkable)
.with_child(self.make_label("Multiple Expanded with varying flex amounts".to_owned()))
.with_child(multiple_expanded_row)
.with_child(self.make_label("Multiple Expanded with constraints in Flex with MainAxisSize::Min".to_owned()))
.with_child(multiple_expanded_with_constraints_row)
.with_child(self.make_label("Multiple Expanded with constraints and varying flex amounts in Flex with MainAxisSize::Min".to_owned()))
.with_child(self.make_label("Note that this results in children beginning to shrink at different times and rates".to_owned()))
.with_child(multiple_expanded_with_constraints_varying_flex_row)
.finish(),
)
.with_margin_top(32.)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
+36
View File
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
+479
View File
@@ -0,0 +1,479 @@
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
elements::{
Border, ConstrainedBox, Container, Flex, MainAxisAlignment, MainAxisSize, ParentElement,
Rect, Shrinkable, Stack, Text, Wrap,
},
AppContext, Element, Entity, TypedActionView, View, ViewContext,
};
use galaxyui::color::ColorU;
pub struct RootView {
font_family: FamilyId,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let font_family = galaxyui::fonts::Cache::handle(ctx)
.update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
RootView { font_family }
}
fn make_label(&self, label: String) -> Box<dyn Element> {
Flex::row()
.with_child(Text::new_inline(label, self.font_family, 16.).finish())
.finish()
}
fn make_row(&self) -> Flex {
Flex::row()
.with_spacing(20.)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("1", self.font_family, 16.).finish())
.with_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("2", self.font_family, 16.).finish())
.with_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 255, 0, 255))
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("3", self.font_family, 16.).finish())
.with_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.with_main_axis_size(MainAxisSize::Max)
}
fn make_wrap_row(&self) -> Wrap {
Wrap::row()
.with_spacing(20.)
.with_run_spacing(10.)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("1", self.font_family, 16.).finish())
.with_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("2", self.font_family, 16.).finish())
.with_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 255, 0, 255))
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("3", self.font_family, 16.).finish())
.with_width(200.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
}
fn make_column(&self) -> Flex {
Flex::column()
.with_spacing(20.)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("1", self.font_family, 16.).finish())
.with_width(20.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("2", self.font_family, 16.).finish())
.with_width(20.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 255, 0, 255))
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(Text::new_inline("3", self.font_family, 16.).finish())
.with_width(20.)
.with_height(50.)
.finish(),
)
.with_background_color(ColorU::new(0, 0, 255, 255))
.finish(),
)
.with_main_axis_size(MainAxisSize::Max)
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
// Let's render a simple black rect background.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
let row_between = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_between_reverse = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_evenly = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_main_axis_alignment(MainAxisAlignment::SpaceEvenly)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_evenly_reverse = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::SpaceEvenly)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_center = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_main_axis_alignment(MainAxisAlignment::Center)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_center_reverse = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::Center)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_start = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_start_reverse = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_end = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_main_axis_alignment(MainAxisAlignment::End)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let row_end_reverse = Container::new(
Shrinkable::new(
1.,
self.make_row()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::End)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_between = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_between_reverse = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_evenly = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_main_axis_alignment(MainAxisAlignment::SpaceEvenly)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_evenly_reverse = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::SpaceEvenly)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_center = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_main_axis_alignment(MainAxisAlignment::Center)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_center_reverse = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::Center)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_start = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_start_reverse = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_end = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_main_axis_alignment(MainAxisAlignment::End)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let column_end_reverse = Container::new(
Shrinkable::new(
1.,
self.make_column()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::End)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let wrap_row = Container::new(
Shrinkable::new(
1.,
self.make_wrap_row()
.with_main_axis_alignment(MainAxisAlignment::SpaceEvenly)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
let wrap_row_reverse = Container::new(
Shrinkable::new(
1.,
self.make_wrap_row()
.with_reverse_orientation()
.with_main_axis_alignment(MainAxisAlignment::SpaceEvenly)
.finish(),
)
.finish(),
)
.with_border(Border::all(2.).with_border_color(ColorU::white()))
.finish();
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(
Flex::column()
.with_child(self.make_label("Space Between".to_owned()))
.with_child(row_between)
.with_child(row_between_reverse)
.with_child(self.make_label("Space Evenly".to_owned()))
.with_child(row_evenly)
.with_child(row_evenly_reverse)
.with_child(self.make_label("Center".to_owned()))
.with_child(row_center)
.with_child(row_center_reverse)
.with_child(self.make_label("Start".to_owned()))
.with_child(row_start)
.with_child(row_start_reverse)
.with_child(self.make_label("End".to_owned()))
.with_child(row_end)
.with_child(row_end_reverse)
.with_child(self.make_label("Wrap Row".to_owned()))
.with_child(wrap_row)
.with_child(wrap_row_reverse)
.with_child(
ConstrainedBox::new(
Flex::row()
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_main_axis_size(MainAxisSize::Max)
.with_child(self.make_label("Space Between ->".to_owned()))
.with_child(column_between)
.with_child(column_between_reverse)
.with_child(self.make_label("Space Evenly ->".to_owned()))
.with_child(column_evenly)
.with_child(column_evenly_reverse)
.with_child(self.make_label("Center ->".to_owned()))
.with_child(column_center)
.with_child(column_center_reverse)
.with_child(self.make_label("Start ->".to_owned()))
.with_child(column_start)
.with_child(column_start_reverse)
.with_child(self.make_label("End ->".to_owned()))
.with_child(column_end)
.with_child(column_end_reverse)
.finish(),
)
.with_max_height(300.)
.finish(),
)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,35 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
let view_handle = root_view::RootView::new;
ctx.add_window(galaxyui::AddWindowOptions::default(), view_handle);
});
Ok(())
}
@@ -0,0 +1,178 @@
//! A UI sample demonstrating how the SelectableArea element can be used.
use markdown_parser::{parse_markdown, FormattedTextFragment, FormattedTextLine};
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
elements::{
ChildView, ConstrainedBox, Flex, FormattedTextElement, HeadingFontSizeMultipliers,
ParentElement, Rect, SelectableArea, SelectionHandle, Stack, Text,
},
AppContext, Element, Entity, TypedActionView, View, ViewContext, ViewHandle,
};
use galaxyui::color::ColorU;
use galaxyui::elements::{Align, HighlightedHyperlink, HyperlinkLens};
pub struct RootView {
sub_view: ViewHandle<FormattedTextView>,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let sub_view = ctx.add_view(|ctx| {
let font_family = galaxyui::fonts::Cache::handle(ctx).update(ctx, |cache, _| {
cache.load_system_font("Menlo").expect("Should load Menlo")
});
let view = FormattedTextView {
font_family,
highlighted_link: Default::default(),
selectable_area_state_handle: Default::default(),
};
ctx.focus_self();
view
});
Self { sub_view }
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _ctx: &AppContext) -> Box<dyn Element> {
ChildView::new(&self.sub_view).finish()
}
}
pub struct FormattedTextView {
font_family: FamilyId,
highlighted_link: HighlightedHyperlink,
selectable_area_state_handle: SelectionHandle,
}
impl Entity for FormattedTextView {
type Event = ();
}
impl View for FormattedTextView {
fn ui_name() -> &'static str {
"SelectableExampleView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(
SelectableArea::new(
self.selectable_area_state_handle.clone(),
|selection_args, _, _| {
println!("Selected text: {:?}", selection_args.selection);
},
Align::new(
ConstrainedBox::new(
Flex::column()
.with_children([
FormattedTextElement::new(
parse_markdown(concat!(
"## This is a markdown header\n",
"### This is a subheader\n",
"This is a ~~strikethrough~~ text.\n",
"* list item 1\n",
"* list item 2\n",
"* list item 3\n",
"* list item 4\n",
"```rust\n",
"fn main() {\n",
" println!(\"Hello, world!\");\n",
"}\n",
"```\n",
"fi\n",
"cd\n",
"this is a [link](https://www.google.com)\n",
))
.unwrap()
.append_line(
FormattedTextLine::Line(vec![
FormattedTextFragment::plain_text(
"\nThis is a link that dispatches an action: ",
),
FormattedTextFragment::hyperlink_action(
"press enter",
RootViewAction::LinkClicked,
),
]),
),
13.,
self.font_family,
self.font_family,
ColorU::white(),
self.highlighted_link.clone(),
)
.with_line_height_ratio(1.2)
.with_heading_to_font_size_multipliers(
HeadingFontSizeMultipliers {
h1: 1.8,
h2: 1.5,
h3: 1.2,
..Default::default()
},
)
.register_default_click_handlers_with_action_support(
|hyperlink_lens, evt, ctx| match hyperlink_lens {
HyperlinkLens::Url(url) => {
ctx.open_url(url);
}
HyperlinkLens::Action(action_ref) => {
if let Some(root_action) = action_ref
.as_any()
.downcast_ref::<RootViewAction>(
) {
evt.dispatch_typed_action(root_action.clone());
}
}
},
)
.set_selectable(true)
.finish(),
Text::new(
"This is normal Text (large font, not a header)",
self.font_family,
13. * 1.5,
)
.finish(),
])
.finish(),
)
.with_max_width(700.)
.finish(),
)
.finish(),
)
.finish(),
)
.finish()
}
}
#[derive(Debug, Clone)]
pub enum RootViewAction {
LinkClicked,
}
impl TypedActionView for RootView {
type Action = RootViewAction;
fn handle_action(&mut self, action: &Self::Action, _ctx: &mut ViewContext<Self>) {
match action {
RootViewAction::LinkClicked => {
println!("Link clicked");
}
}
}
}
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
pub static ASSETS: Assets = Assets;
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
env_logger::init();
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,289 @@
use image::ImageEncoder;
use pathfinder_color::ColorU;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};
use galaxyui::SingletonEntity as _;
use galaxyui::{
elements::{
Align, ConstrainedBox, Container, DispatchEventResult, EventHandler, Padding,
ParentElement, Rect, Stack, Text,
},
fonts::{Cache as FontCache, FamilyId},
platform::CapturedFrame,
AppContext, Element, Entity, TypedActionView, View, ViewContext,
};
#[derive(Clone, Debug)]
pub enum RootViewAction {
CaptureFrame,
}
pub struct RootView {
window_id: galaxyui::WindowId,
font_family: FamilyId,
last_capture_msg: Arc<Mutex<Option<String>>>,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let window_id = ctx.window_id();
let font_family = FontCache::handle(ctx)
.update(ctx, |cache: &mut FontCache, _| {
cache.load_system_font("Arial").ok()
})
.unwrap_or(FamilyId(0));
log::info!("Frame capture demo initialized. Click the button to capture!");
println!("\n📸 Click the blue button to capture the frame!\n");
Self {
window_id,
font_family,
last_capture_msg: Arc::new(Mutex::new(None)),
}
}
fn request_capture(&mut self, ctx: &mut ViewContext<Self>) {
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let filename = format!("frame_capture_{}.png", timestamp);
log::info!("Requesting frame capture to: {}", filename);
println!("\n📸 Requesting frame capture to: {}\n", filename);
*self.last_capture_msg.lock().unwrap() = Some("Capture requested...".to_string());
ctx.notify();
if let Some(window) = ctx.windows().platform_window(self.window_id) {
let msg_handle = Arc::clone(&self.last_capture_msg);
window
.as_ctx()
.request_frame_capture(Box::new(move |frame| {
log::info!("Frame captured, saving to file");
match save_frame_as_png(&frame, &filename) {
Ok(()) => {
log::info!("Frame saved to: {}", filename);
println!("\n✅ Frame saved to: {}\n", filename);
*msg_handle.lock().unwrap() =
Some(format!("Frame written to {}", filename));
}
Err(e) => {
log::error!("Failed to save frame: {}", e);
*msg_handle.lock().unwrap() = Some(format!("Error: {}", e));
}
}
}));
}
}
}
fn save_frame_as_png(frame: &CapturedFrame, path: &str) -> Result<(), Box<dyn std::error::Error>> {
let file = std::fs::File::create(path)?;
let mut writer = std::io::BufWriter::new(file);
let encoder = image::codecs::png::PngEncoder::new_with_quality(
&mut writer,
image::codecs::png::CompressionType::Fast,
image::codecs::png::FilterType::NoFilter,
);
encoder.write_image(
&frame.data,
frame.width,
frame.height,
image::ColorType::Rgba8.into(),
)?;
Ok(())
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
let button_color = ColorU::new(70, 120, 200, 255);
let status_msg = self
.last_capture_msg
.lock()
.ok()
.and_then(|guard| guard.clone())
.unwrap_or_else(|| "Click the button below to write a frame".to_string());
Stack::new()
// Dark background
.with_child(
Rect::new()
.with_background_color(ColorU::new(40, 40, 45, 255))
.finish(),
)
// Content: Colorful squares arranged vertically
.with_child(
Align::new(
Container::new(
Stack::new()
// Title text
.with_child(
Container::new(
Text::new_inline(
"Frame Capture Test".to_string(),
self.font_family,
28.0,
)
.with_color(ColorU::white())
.finish(),
)
.with_padding(Padding::uniform(16.0))
.finish(),
)
// Subtitle
.with_child(
Container::new(
Text::new_inline(
"WarpUI rendering sample with clickable capture button"
.to_string(),
self.font_family,
16.0,
)
.with_color(ColorU::new(200, 200, 200, 255))
.finish(),
)
.with_padding(Padding::uniform(12.0))
.finish(),
)
// Toast / status line
.with_child(
Container::new(
Text::new_inline(status_msg, self.font_family, 14.0)
.with_color(ColorU::new(180, 220, 180, 255))
.finish(),
)
.with_padding(Padding::uniform(12.0))
.finish(),
)
// Red square
.with_child(
Container::new(
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(255, 100, 100, 255))
.finish(),
)
.with_width(150.0)
.with_height(150.0)
.finish(),
)
.with_padding(Padding::uniform(15.0))
.finish(),
)
// Green square
.with_child(
Container::new(
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(100, 255, 100, 255))
.finish(),
)
.with_width(150.0)
.with_height(150.0)
.finish(),
)
.with_padding(Padding::uniform(15.0))
.finish(),
)
// Blue square
.with_child(
Container::new(
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(100, 100, 255, 255))
.finish(),
)
.with_width(150.0)
.with_height(150.0)
.finish(),
)
.with_padding(Padding::uniform(15.0))
.finish(),
)
// Orange square
.with_child(
Container::new(
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(255, 165, 0, 255))
.finish(),
)
.with_width(150.0)
.with_height(150.0)
.finish(),
)
.with_padding(Padding::uniform(15.0))
.finish(),
)
// Capture button (purple square)
.with_child(
Container::new(
EventHandler::new(
ConstrainedBox::new(
Stack::new()
// Button background
.with_child(
Rect::new()
.with_background_color(button_color)
.finish(),
)
// Button label
.with_child(
Align::new(
Text::new_inline(
"Write Frame to File System"
.to_string(),
self.font_family,
16.0,
)
.with_color(ColorU::white())
.finish(),
)
.finish(),
)
.finish(),
)
.with_width(200.0)
.with_height(80.0)
.finish(),
)
.on_left_mouse_down(|ctx, _, _| {
ctx.dispatch_typed_action(RootViewAction::CaptureFrame);
DispatchEventResult::StopPropagation
})
.finish(),
)
.with_padding(Padding::uniform(15.0))
.finish(),
)
.finish(),
)
.with_padding(Padding::uniform(40.0))
.finish(),
)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = RootViewAction;
fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
match action {
RootViewAction::CaptureFrame => self.request_capture(ctx),
}
}
}
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="#13D3D3" xmlns="http://www.w3.org/2000/svg">
<path fill="#13D3D3" fill-rule="evenodd" clip-rule="evenodd" d="M11.614 1.05689C11.377 1.13089 11.212 1.26089 11.109 1.45289C11.02 1.61889 11.02 1.62889 11.009 3.91089C11.001 5.39189 11.012 6.25589 11.039 6.35489C11.144 6.74689 11.433 6.96089 11.896 6.99089C12.381 7.02189 12.699 6.88189 12.88 6.55789L12.98 6.37989V3.99989V1.61989L12.891 1.45289C12.842 1.36189 12.746 1.24689 12.678 1.19889C12.414 1.00989 11.957 0.949891 11.614 1.05689ZM4.54602 4.04489C4.34302 4.15189 4.06602 4.43189 3.96302 4.63489C3.85302 4.84889 3.85402 5.15089 3.96402 5.37189C4.09202 5.62789 7.16402 8.69089 7.40002 8.79689C7.76202 8.95989 8.13302 8.86189 8.46602 8.51289C8.79602 8.16789 8.87702 7.83489 8.71702 7.47989C8.61102 7.24389 5.54802 4.17189 5.29202 4.04389C5.07202 3.93389 4.75502 3.93489 4.54602 4.04489ZM18.7 4.04589C18.605 4.09489 17.892 4.77489 16.952 5.71389C15.277 7.38689 15.203 7.47589 15.201 7.82489C15.2 8.05489 15.293 8.25489 15.511 8.48789C15.86 8.85989 16.228 8.96489 16.6 8.79689C16.836 8.69089 19.908 5.62789 20.036 5.37189C20.212 5.01989 20.121 4.63889 19.781 4.29889C19.441 3.95889 19.047 3.86689 18.7 4.04589ZM1.61402 11.0569C1.37702 11.1309 1.21202 11.2609 1.10902 11.4529C1.03502 11.5919 1.02002 11.6839 1.02002 11.9999C1.02002 12.3309 1.03302 12.4029 1.12002 12.5579C1.23002 12.7549 1.41402 12.8979 1.63502 12.9589C1.83102 13.0139 6.16902 13.0139 6.36502 12.9589C6.58602 12.8979 6.77002 12.7549 6.88002 12.5579C6.96702 12.4019 6.98002 12.3319 6.97902 11.9999C6.97802 11.5669 6.89802 11.3549 6.67802 11.1989C6.40602 11.0049 6.33602 10.9999 3.98402 11.0029C2.29302 11.0049 1.74102 11.0169 1.61402 11.0569ZM17.614 11.0569C17.377 11.1309 17.212 11.2609 17.109 11.4529C17.035 11.5919 17.02 11.6839 17.02 11.9999C17.02 12.3309 17.033 12.4029 17.12 12.5579C17.23 12.7549 17.414 12.8979 17.635 12.9589C17.831 13.0139 22.169 13.0139 22.365 12.9589C22.586 12.8979 22.77 12.7549 22.88 12.5579C22.967 12.4019 22.98 12.3319 22.979 11.9999C22.978 11.5669 22.898 11.3549 22.678 11.1989C22.406 11.0049 22.336 10.9999 19.984 11.0029C18.293 11.0049 17.741 11.0169 17.614 11.0569ZM7.40002 15.2829C7.15902 15.3909 4.09202 18.4529 3.96002 18.7159C3.78502 19.0669 3.87502 19.4369 4.21902 19.7809C4.55902 20.1209 4.94002 20.2119 5.29202 20.0359C5.54802 19.9079 8.61102 16.8359 8.71702 16.5999C8.87702 16.2449 8.80602 15.9309 8.48902 15.5919C8.14002 15.2199 7.77202 15.1149 7.40002 15.2829ZM15.879 15.2979C15.659 15.4129 15.347 15.7379 15.262 15.9409C15.178 16.1429 15.185 16.3839 15.283 16.5999C15.389 16.8359 18.452 19.9079 18.708 20.0359C19.06 20.2119 19.441 20.1209 19.781 19.7809C20.121 19.4409 20.212 19.0599 20.036 18.7079C19.908 18.4519 16.836 15.3889 16.6 15.2829C16.36 15.1749 16.105 15.1799 15.879 15.2979ZM11.614 17.0569C11.377 17.1309 11.212 17.2609 11.109 17.4529C11.02 17.6189 11.02 17.6289 11.009 19.9109C11.001 21.3919 11.012 22.2559 11.039 22.3549C11.144 22.7469 11.433 22.9609 11.896 22.9909C12.381 23.0219 12.699 22.8819 12.88 22.5579L12.98 22.3799V19.9999V17.6199L12.891 17.4529C12.842 17.3619 12.746 17.2469 12.678 17.1989C12.414 17.0099 11.957 16.9499 11.614 17.0569Z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

+16
View File
@@ -0,0 +1,16 @@
use anyhow::Result;
use root_view::RootView;
pub mod root_view;
extern crate galaxyui;
use galaxyui::platform;
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(()), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(galaxyui::AddWindowOptions::default(), |_| RootView::new());
});
Ok(())
}
@@ -0,0 +1,73 @@
use pathfinder_color::ColorU;
use galaxyui::{
elements::{
CacheOption, ConstrainedBox, Flex, Icon, Image, MainAxisAlignment, MainAxisSize,
ParentElement, Rect, Stack,
},
AppContext, Element, Entity, TypedActionView, View,
};
pub struct RootView {}
impl RootView {
pub fn new() -> Self {
RootView {}
}
}
impl Default for RootView {
fn default() -> Self {
Self::new()
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
let asset_source = ::asset_cache::url_source(
"https://i.ebayimg.com/images/g/B~gAAOSwhNthhdjn/s-l1600.jpg",
);
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::white()).finish())
.with_child(
Flex::column()
.with_main_axis_alignment(MainAxisAlignment::Center)
.with_main_axis_size(MainAxisSize::Max)
.with_child(
Flex::row()
.with_main_axis_alignment(MainAxisAlignment::Center)
.with_main_axis_size(MainAxisSize::Max)
.with_child(
ConstrainedBox::new(
Image::new(asset_source, CacheOption::BySize)
.before_load(
Icon::new(
"ui/examples/image/loading.svg",
ColorU::black(),
)
.finish(),
)
.finish(),
)
.with_height(500.)
.with_width(500.)
.finish(),
)
.finish(),
)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
+36
View File
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
+208
View File
@@ -0,0 +1,208 @@
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
elements::{
Border, ConstrainedBox, Container, Fill, Flex, List, ListState, MainAxisSize,
ParentElement, Rect, ScrollStateHandle, Scrollable, ScrollableElement, ScrollbarWidth,
Stack, Text,
},
AppContext, Element, Entity, TypedActionView, View, ViewContext,
};
use std::sync::{Arc, Mutex};
use galaxyui::color::ColorU;
pub struct RootView {
font_family: FamilyId,
list_state: ListState<()>,
scroll_state: ScrollStateHandle,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let font_family = galaxyui::fonts::Cache::handle(ctx)
.update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
let list_state = ListState::new(move |i, _scroll_offset, _app| {
println!(" 📦 Creating element for item {i}"); // This should only appear for visible items!
Self::make_list_item(i, font_family).finish()
});
let scroll_state = Arc::new(Mutex::new(Default::default()));
// Add many items to demonstrate viewporting - only visible ones should be rendered
println!("Creating List with 1000 items...");
for _ in 0..=1000 {
list_state.add_item();
}
println!(
"✅ All 1000 items added to list state. Now only visible ones should be rendered."
);
RootView {
font_family,
list_state,
scroll_state,
}
}
fn make_list_item(index: usize, font_family: FamilyId) -> Container {
// Alternate colors to make it easy to see which items are rendered
let bg_color = if index.is_multiple_of(2) {
ColorU::new(240, 240, 240, 255) // Light gray
} else {
ColorU::new(255, 255, 255, 255) // White
};
let border_color = if index.is_multiple_of(10) {
ColorU::new(255, 0, 0, 255) // Red border for every 10th item
} else {
ColorU::new(200, 200, 200, 255) // Light gray border
};
let height = 50. * (index + 1) as f32;
Container::new(
ConstrainedBox::new(
Flex::row()
.with_child(
Text::new_inline(format!("Item #{index}"), font_family, 16.)
.with_color(ColorU::black())
.finish(),
)
.with_child(
Container::new(
ConstrainedBox::new(
Text::new_inline(
if index.is_multiple_of(10) {
" (MILESTONE)".to_string()
} else {
format!(" - Height: {height}px")
},
font_family,
14.,
)
.with_color(ColorU::black())
.finish(),
)
.with_width(200.)
.finish(),
)
.finish(),
)
.with_main_axis_size(MainAxisSize::Max)
.finish(),
)
.with_width(600.)
.with_height(height)
.finish(),
)
.with_background_color(bg_color)
.with_border(Border::all(1.).with_border_color(border_color))
}
fn make_instructions(&self) -> Box<dyn Element> {
Container::new(
ConstrainedBox::new(
Flex::column()
.with_child(
Text::new_inline(
"List Demo - 1000 Items".to_string(),
self.font_family,
20.,
)
.with_color(ColorU::black())
.finish(),
)
.with_child(
Text::new_inline(
"This demonstrates viewporting: only visible items are rendered!"
.to_string(),
self.font_family,
14.,
)
.with_color(ColorU::black())
.finish(),
)
.with_child(
Text::new_inline(
"🔍 Check the console output to see which items are being rendered."
.to_string(),
self.font_family,
14.,
)
.with_color(ColorU::black())
.finish(),
)
.with_child(
Text::new_inline(
"🟥 Red borders mark milestone items (every 10th).".to_string(),
self.font_family,
14.,
)
.with_color(ColorU::black())
.finish(),
)
.with_child(
Text::new_inline(
"📏 Viewport height: 400px.".to_string(),
self.font_family,
12.,
)
.with_color(ColorU::black())
.finish(),
)
.finish(),
)
.with_height(140.)
.finish(),
)
.with_background_color(ColorU::new(230, 230, 255, 255))
.with_border(Border::all(2.).with_border_color(ColorU::new(100, 100, 200, 255)))
.finish()
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"ListRootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::white()).finish())
.with_child(
Flex::column()
.with_child(self.make_instructions())
.with_child(
Container::new(
ConstrainedBox::new(
Scrollable::vertical(
self.scroll_state.clone(),
List::new(self.list_state.clone()).finish_scrollable(),
ScrollbarWidth::Auto,
Fill::Solid(ColorU::new(150, 150, 150, 255)), // Non-active thumb
Fill::Solid(ColorU::new(100, 100, 100, 255)), // Active thumb
Fill::Solid(ColorU::new(240, 240, 240, 255)), // Track background
)
.finish(),
)
.with_height(400.) // Constrain the viewport height
.finish(),
)
.with_background_color(ColorU::new(250, 250, 250, 255))
.with_border(Border::all(2.).with_border_color(ColorU::black()))
.finish(),
)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,37 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
root_view::init(ctx);
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,249 @@
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::vec2f;
use pathfinder_geometry::vector::Vector2F;
use galaxyui::elements::new_scrollable::AxisConfiguration;
use galaxyui::elements::new_scrollable::ClippedAxisConfiguration;
use galaxyui::elements::new_scrollable::DualAxisConfig;
use galaxyui::elements::new_scrollable::NewScrollableElement;
use galaxyui::elements::new_scrollable::ScrollableAppearance;
use galaxyui::elements::new_scrollable::ScrollableAxis;
use galaxyui::elements::Axis;
use galaxyui::elements::ChildView;
use galaxyui::elements::ClippedScrollStateHandle;
use galaxyui::elements::NewScrollable;
use galaxyui::elements::Point;
use galaxyui::elements::ScrollData;
use galaxyui::elements::ScrollStateHandle;
use galaxyui::keymap::FixedBinding;
use galaxyui::units::Pixels;
use galaxyui::AppContext;
use galaxyui::TypedActionView;
use galaxyui::ViewHandle;
use galaxyui::{
elements::{ConstrainedBox, ParentElement, Rect, ScrollbarWidth, Stack},
Element, Entity, View, ViewContext,
};
use galaxyui::color::ColorU;
pub fn init(ctx: &mut AppContext) {
use galaxyui::keymap::macros::*;
// Add bindings to trigger actions in the subview.
ctx.register_fixed_bindings([
FixedBinding::new("up", SubViewAction::ScrollVertical(50.), id!("SubView")),
FixedBinding::new("down", SubViewAction::ScrollVertical(-50.), id!("SubView")),
]);
}
pub struct RootView {
// RootView "owns" a viewhandle to the subview.
sub_view: ViewHandle<SubView>,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
// Adding typed action view allows the view to receive keydown events.
let sub_view = ctx.add_typed_action_view(|ctx| {
let view = SubView::default();
// Need the view to be focused for keydown actions to be dispatched to it.
ctx.focus_self();
view
});
Self { sub_view }
}
}
// Implement the entity trait.
impl Entity for RootView {
type Event = ();
}
// Implement the view trait so RootView could be considered as a view.
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
// Renders the child view of sub_view.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
ChildView::new(&self.sub_view).finish()
}
}
#[derive(Debug, Clone)]
pub enum SubViewAction {
ScrollVertical(f32),
}
#[derive(Default)]
pub struct SubView {
pub scroll_state_horizontal: ClippedScrollStateHandle,
pub scroll_state_vertical: ScrollStateHandle,
pub scroll_top: f32,
}
impl SubView {
pub fn new(_ctx: &mut ViewContext<Self>) -> Self {
SubView::default()
}
}
impl Entity for SubView {
type Event = ();
}
impl View for SubView {
fn ui_name() -> &'static str {
"SubView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
let axis_config = DualAxisConfig::Manual {
horizontal: AxisConfiguration::Clipped(ClippedAxisConfiguration {
handle: self.scroll_state_horizontal.clone(),
max_size: None,
stretch_child: false,
}),
vertical: AxisConfiguration::Manual(self.scroll_state_vertical.clone()),
child: ScrollableElement::new(self.scroll_top).finish_scrollable(),
};
let horizontally_scrollable = NewScrollable::horizontal_and_vertical(
axis_config,
ColorU::new(255, 255, 255, 150).into(),
ColorU::white().into(),
ColorU::new(100, 100, 100, 255).into(),
)
.with_horizontal_scrollbar(ScrollableAppearance::new(ScrollbarWidth::Auto, true))
.with_vertical_scrollbar(ScrollableAppearance::new(ScrollbarWidth::Auto, false));
let constrained = ConstrainedBox::new(horizontally_scrollable.finish())
.with_height(250.)
.with_width(250.);
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(constrained.finish())
.finish()
}
}
impl TypedActionView for SubView {
type Action = SubViewAction;
fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
match action {
SubViewAction::ScrollVertical(scroll_top) => {
// 250. viewport + 7. scrollbar width.
self.scroll_top = (self.scroll_top - *scroll_top).clamp(0., 257.);
}
};
ctx.notify();
}
}
struct ScrollableElement {
size: Option<Vector2F>,
origin: Option<Point>,
scroll_top: f32,
}
impl ScrollableElement {
fn new(scroll_top: f32) -> Self {
Self {
scroll_top,
size: None,
origin: None,
}
}
}
impl Element for ScrollableElement {
fn layout(
&mut self,
constraint: galaxyui::SizeConstraint,
_: &mut galaxyui::LayoutContext,
_: &AppContext,
) -> Vector2F {
let size = vec2f(
constraint.max_along(Axis::Horizontal).min(500.),
constraint.max_along(Axis::Vertical).min(500.),
);
self.size = Some(size);
size
}
fn after_layout(&mut self, _: &mut galaxyui::AfterLayoutContext, _: &AppContext) {}
fn paint(&mut self, origin: Vector2F, ctx: &mut galaxyui::PaintContext, _app: &AppContext) {
self.origin = Some(Point::from_vec2f(origin, ctx.scene.z_index()));
let adjusted_origin = origin - vec2f(0., self.scroll_top);
for i in 0..10 {
for j in 0..10 {
let color = (i + j) % 3;
let color = if color == 0 {
ColorU::new(255, 0, 0, 255)
} else if color == 1 {
ColorU::new(0, 255, 0, 255)
} else {
ColorU::new(0, 0, 255, 255)
};
let cell_origin = adjusted_origin + vec2f(i as f32 * 50., j as f32 * 50.);
ctx.scene
.draw_rect_with_hit_recording(RectF::new(cell_origin, vec2f(50., 50.)))
.with_background(color);
}
}
}
fn size(&self) -> Option<Vector2F> {
self.size
}
fn origin(&self) -> Option<Point> {
self.origin
}
fn dispatch_event(
&mut self,
_: &galaxyui::event::DispatchedEvent,
_: &mut galaxyui::EventContext,
_: &AppContext,
) -> bool {
false
}
}
impl NewScrollableElement for ScrollableElement {
fn axis(&self) -> ScrollableAxis {
ScrollableAxis::Vertical
}
fn scroll_data(&self, axis: Axis, _app: &AppContext) -> Option<ScrollData> {
match axis {
Axis::Horizontal => None,
Axis::Vertical => Some(ScrollData {
scroll_start: Pixels::new(self.scroll_top),
visible_px: Pixels::new(self.size.unwrap().y()),
total_size: Pixels::new(500.),
}),
}
}
fn scroll(&mut self, delta: galaxyui::units::Pixels, axis: Axis, ctx: &mut galaxyui::EventContext) {
match axis {
Axis::Horizontal => (),
Axis::Vertical => {
ctx.dispatch_typed_action(SubViewAction::ScrollVertical(delta.as_f32()))
}
}
}
fn axis_should_handle_scroll_wheel(&self, _axis: Axis) -> bool {
true
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,145 @@
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
color::ColorU,
elements::{
Align, Border, ConstrainedBox, Container, CrossAxisAlignment, Flex, MainAxisAlignment,
MainAxisSize, ParentElement, Percentage, Rect, Shrinkable, Text,
},
AppContext, Element, Entity, TypedActionView, View, ViewContext,
};
pub struct RootView {
font_family: FamilyId,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let font_family = galaxyui::fonts::Cache::handle(ctx)
.update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
RootView { font_family }
}
fn make_label(&self, text: &str) -> Box<dyn Element> {
Text::new_inline(text.to_string(), self.font_family, 16.).finish()
}
fn make_width(&self) -> Box<dyn Element> {
let bar = |pct: f32, color: ColorU| {
Shrinkable::new(
1.,
Container::new(
ConstrainedBox::new(
Align::new(
Percentage::width(
pct,
Rect::new().with_background_color(color).finish(),
)
.finish(),
)
.left()
.finish(),
)
.with_height(12.)
.finish(),
)
.with_border(Border::all(1.).with_border_color(ColorU::white()))
.finish(),
)
.finish()
};
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_child(self.make_label("Width 25/50/75%:"))
.with_child(bar(0.25, ColorU::new(200, 80, 80, 255)))
.with_child(bar(0.50, ColorU::new(80, 200, 80, 255)))
.with_child(bar(0.75, ColorU::new(80, 120, 220, 255)))
.finish()
}
fn make_height(&self) -> Box<dyn Element> {
let bar = |pct: f32, color: ColorU| {
Shrinkable::new(
1.,
Container::new(
Align::new(
Percentage::height(pct, Rect::new().with_background_color(color).finish())
.finish(),
)
.top_left()
.finish(),
)
.with_border(Border::all(1.).with_border_color(ColorU::white()))
.finish(),
)
.finish()
};
Shrinkable::new(
1.,
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::End)
.with_spacing(12.)
.with_child(self.make_label("Height 30/60/90%:"))
.with_child(bar(0.30, ColorU::new(200, 80, 80, 255)))
.with_child(bar(0.60, ColorU::new(80, 200, 80, 255)))
.with_child(bar(0.90, ColorU::new(80, 120, 220, 255)))
.finish(),
)
.finish()
}
fn make_both(&self) -> Box<dyn Element> {
let cell = |w: f32, h: f32, color: ColorU| {
let child =
Percentage::both(w, h, Rect::new().with_background_color(color).finish()).finish();
Shrinkable::new(
1.,
Container::new(Align::new(child).top_left().finish())
.with_border(Border::all(1.).with_border_color(ColorU::white()))
.finish(),
)
.finish()
};
Shrinkable::new(
1.,
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_spacing(12.)
.with_child(self.make_label("Both 60% x 50%, 80% x 30%:"))
.with_child(cell(0.6, 0.5, ColorU::new(200, 80, 80, 255)))
.with_child(cell(0.8, 0.3, ColorU::new(80, 200, 80, 255)))
.finish(),
)
.finish()
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Container::new(
Flex::column()
.with_spacing(16.)
.with_child(self.make_width())
.with_child(self.make_height())
.with_child(self.make_both())
.with_main_axis_size(MainAxisSize::Max)
.with_main_axis_alignment(MainAxisAlignment::Start)
.finish(),
)
.with_background_color(ColorU::black())
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,177 @@
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
color::ColorU,
elements::{
resizable_state_handle, Container, CrossAxisAlignment, DragBarSide, Flex,
MainAxisAlignment, MainAxisSize, ParentElement, Rect, Resizable, ResizableStateHandle,
Shrinkable, Stack, Text,
},
AppContext, Element, Entity, TypedActionView, View, ViewContext,
};
pub struct RootView {
font_family: FamilyId,
left_panel_state: ResizableStateHandle,
top_panel_state: ResizableStateHandle,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let font_family = galaxyui::fonts::Cache::handle(ctx)
.update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
// Initialize resizable state handles
let left_panel_state = resizable_state_handle(250.0);
let top_panel_state = resizable_state_handle(150.0);
RootView {
font_family,
left_panel_state,
top_panel_state,
}
}
fn make_panel_content(&self, text: String, color: ColorU) -> Box<dyn Element> {
Container::new(
Flex::column()
.with_main_axis_alignment(MainAxisAlignment::Center)
.with_main_axis_size(MainAxisSize::Max)
.with_cross_axis_alignment(CrossAxisAlignment::Stretch)
.with_child(
Text::new_inline(text, self.font_family, 16.)
.with_color(ColorU::white())
.finish(),
)
.finish(),
)
.with_background_color(color)
.with_uniform_padding(10.)
.finish()
}
fn make_info_text(&self, text: String) -> Box<dyn Element> {
Text::new_inline(text, self.font_family, 14.)
.with_color(ColorU::white())
.finish()
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
// Create main column for vertical layout
let mut main_column = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch);
// Top panel with bottom-side dragbar for vertical resizing
let top_panel = Container::new(
Resizable::new(
self.top_panel_state.clone(),
self.make_panel_content(
"Top Panel\n(Drag bottom edge)".to_string(),
ColorU::new(200, 100, 200, 255),
),
)
.with_dragbar_side(DragBarSide::Bottom)
.with_dragbar_color(galaxyui::elements::Fill::Solid(ColorU::new(0, 255, 255, 200)))
.on_resize(move |ctx, _| {
ctx.notify();
})
.on_start_resizing(|_, _| {
eprintln!("Top panel: Started resizing");
})
.on_end_resizing(|_, _| {
eprintln!("Top panel: Finished resizing");
})
.with_bounds_callback(Box::new(|window_size| {
let min_height = 100.0;
let max_height = window_size.y() * 0.5;
(min_height, max_height.max(min_height))
}))
.finish(),
)
.finish();
main_column.add_child(top_panel);
// Create the row with CrossAxisAlignment::Stretch
let mut main_row = Flex::row().with_cross_axis_alignment(CrossAxisAlignment::Stretch);
// Left panel with right-side dragbar - added directly to the row
let left_panel = Container::new(
Resizable::new(
self.left_panel_state.clone(),
self.make_panel_content(
"Left Panel\n(Drag right edge)".to_string(),
ColorU::new(100, 100, 200, 255),
),
)
.with_dragbar_side(DragBarSide::Right)
.with_dragbar_color(galaxyui::elements::Fill::Solid(ColorU::new(255, 255, 0, 200)))
.on_resize(move |ctx, _| {
ctx.notify();
})
.on_start_resizing(|_, _| {
eprintln!("Left panel: Started resizing");
})
.on_end_resizing(|_, _| {
eprintln!("Left panel: Finished resizing");
})
.with_bounds_callback(Box::new(|window_size| {
let min_width = 150.0;
let max_width = window_size.x() * 0.6;
(min_width, max_width.max(min_width))
}))
.finish(),
)
.finish();
main_row.add_child(left_panel);
// Right content area - wrapped in Shrinkable to fill remaining space
let right_content = Container::new(
Flex::column()
.with_main_axis_alignment(MainAxisAlignment::Center)
.with_main_axis_size(MainAxisSize::Max)
.with_cross_axis_alignment(CrossAxisAlignment::Stretch)
.with_spacing(20.)
.with_child(
Text::new_inline("Resizable Example", self.font_family, 24.)
.with_color(ColorU::white())
.finish(),
)
.with_child(self.make_info_text("Yellow bar: Horizontal resize (left)".to_string()))
.with_child(self.make_info_text("Cyan bar: Vertical resize (top)".to_string()))
.with_child(self.make_info_text("Check terminal for events".to_string()))
.finish(),
)
.with_background_color(ColorU::new(50, 50, 50, 255))
.with_uniform_padding(20.)
.finish();
main_row.add_child(Shrinkable::new(1.0, right_content).finish());
main_column.add_child(Shrinkable::new(1.0, main_row.finish()).finish());
// Main layout
Stack::new()
.with_child(
Rect::new()
.with_background_color(ColorU::new(30, 30, 30, 255))
.finish(),
)
.with_child(Shrinkable::new(1.0, main_column.finish()).finish())
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,84 @@
use galaxyui::elements::ClippedScrollStateHandle;
use galaxyui::elements::ClippedScrollable;
use galaxyui::{
elements::{ConstrainedBox, Container, Flex, ParentElement, Rect, ScrollbarWidth, Stack},
AppContext, Element, Entity, TypedActionView, View, ViewContext,
};
use galaxyui::color::ColorU;
#[derive(Default)]
pub struct RootView {
pub clipped_scroll_state: ClippedScrollStateHandle,
}
impl RootView {
pub fn new(_ctx: &mut ViewContext<Self>) -> Self {
RootView::default()
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
let mut column = Flex::column();
// Create 10 rows, where each row has 10 rectanges (each of size 50*50).
// By the end, `column` will be 500 * 500.
for i in 0..10 {
let mut row = Flex::row();
for j in 0..10 {
let color = (i + j) % 3;
let color = if color == 0 {
ColorU::new(255, 0, 0, 255)
} else if color == 1 {
ColorU::new(0, 255, 0, 255)
} else {
ColorU::new(0, 0, 255, 255)
};
row.add_child(
Container::new(
ConstrainedBox::new(Rect::new().finish())
.with_height(50.)
.with_width(50.)
.finish(),
)
.with_background_color(color)
.finish(),
);
}
column.add_child(row.finish());
}
// Change this to [`ClippedScrollable::vertical`] to see what a vertically scrollable element looks like.
let horizontally_scrollable = ClippedScrollable::horizontal(
self.clipped_scroll_state.clone(),
column.finish(),
ScrollbarWidth::Auto,
ColorU::white().into(),
ColorU::white().into(),
ColorU::new(100, 100, 100, 255).into(),
);
let constrained = ConstrainedBox::new(horizontally_scrollable.finish())
.with_height(250.)
.with_width(250.);
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(constrained.finish())
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,34 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
pub static ASSETS: Assets = Assets;
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,133 @@
use galaxyui::color::ColorU;
use galaxyui::elements::{
Align, Container, CornerRadius, CrossAxisAlignment, Fill, Flex, ParentElement, Radius, Text,
};
use galaxyui::fonts::FamilyId;
use galaxyui::presenter::ChildView;
use galaxyui::ui_components::components::{Coords, UiComponentStyles};
use galaxyui::ui_components::segmented_control::{
LabelConfig, RenderableOptionConfig, SegmentedControl, SegmentedControlEvent,
};
use galaxyui::SingletonEntity as _;
use galaxyui::{AppContext, Element, Entity, TypedActionView, View, ViewContext, ViewHandle};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisplayMode {
Rendered,
Raw,
}
pub struct RootView {
font_family: FamilyId,
segmented_control: ViewHandle<SegmentedControl<DisplayMode>>,
selected_mode: DisplayMode,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let font_family = galaxyui::fonts::Cache::handle(ctx)
.update(ctx, |cache, _| cache.load_system_font("Arial").unwrap());
let segmented_control = ctx.add_typed_action_view(move |_ctx| {
SegmentedControl::new(
vec![DisplayMode::Rendered, DisplayMode::Raw],
move |mode, is_selected, _app| {
Some(RenderableOptionConfig {
icon_path: "",
icon_color: ColorU::white(),
label: Some(LabelConfig {
label: match mode {
DisplayMode::Rendered => "Rendered".into(),
DisplayMode::Raw => "Raw".into(),
},
width_override: Some(55.0),
color: if is_selected {
ColorU::new(100, 200, 255, 255) // accent color
} else {
ColorU::white()
},
}),
tooltip: None,
background: if is_selected {
Fill::Solid(ColorU::new(60, 60, 60, 255)) // surface_3
} else {
Fill::None
},
})
},
DisplayMode::Rendered,
segmented_control_styles(font_family),
)
});
ctx.subscribe_to_view(&segmented_control, |me, _, event, ctx| {
let SegmentedControlEvent::OptionSelected(mode) = event;
me.selected_mode = *mode;
ctx.notify();
});
Self {
font_family,
segmented_control,
selected_mode: DisplayMode::Rendered,
}
}
}
fn segmented_control_styles(font_family: FamilyId) -> UiComponentStyles {
UiComponentStyles {
font_family_id: Some(font_family),
font_size: Some(12.0),
border_radius: Some(CornerRadius::with_all(Radius::Pixels(4.0))),
border_width: Some(1.0),
border_color: Some(Fill::Solid(ColorU::new(60, 60, 60, 255))), // surface_3
background: Some(Fill::Solid(ColorU::new(30, 30, 30, 255))), // background
height: Some(20.0),
padding: Some(Coords::uniform(2.0)),
margin: Some(Coords {
top: 0.0,
bottom: 0.0,
left: 0.0,
right: 8.0,
}),
..Default::default()
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _app: &AppContext) -> Box<dyn Element> {
let status_text = format!("Selected: {:?}", self.selected_mode);
let content = Flex::column()
.with_spacing(20.0)
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_child(
Text::new_inline("Segmented Control Example", self.font_family, 20.0)
.with_color(ColorU::white())
.finish(),
)
.with_child(ChildView::new(&self.segmented_control).finish())
.with_child(
Text::new_inline(status_text, self.font_family, 14.0)
.with_color(ColorU::new(150, 150, 150, 255))
.finish(),
)
.finish();
Container::new(Align::new(content).finish())
.with_background_color(ColorU::new(20, 20, 20, 255))
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,34 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
let view_handle = root_view::RootView::new;
ctx.add_window(galaxyui::AddWindowOptions::default(), view_handle);
});
Ok(())
}
@@ -0,0 +1,299 @@
//! A UI sample demonstrating how the SelectableArea element can be used.
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
elements::{
Border, ChildView, ConstrainedBox, Container, Flex, ParentElement, Rect, SelectableArea,
SelectionHandle, Stack, Text,
},
AppContext, Element, Entity, TypedActionView, View, ViewContext, ViewHandle,
};
use galaxyui::color::ColorU;
pub struct RootView {
sub_view: ViewHandle<SelectableExampleView>,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let sub_view = ctx.add_view(|ctx| {
let font_family = galaxyui::fonts::Cache::handle(ctx).update(ctx, |cache, _| {
cache.load_system_font("Menlo").expect("Should load Menlo")
});
let view = SelectableExampleView {
font_family,
selectable_area_state_handle_1: Default::default(),
selectable_area_state_handle_2: Default::default(),
};
ctx.focus_self();
view
});
Self { sub_view }
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _ctx: &AppContext) -> Box<dyn Element> {
ChildView::new(&self.sub_view).finish()
}
}
pub struct SelectableExampleView {
font_family: FamilyId,
selectable_area_state_handle_1: SelectionHandle,
selectable_area_state_handle_2: SelectionHandle,
}
impl Entity for SelectableExampleView {
type Event = ();
}
impl View for SelectableExampleView {
fn ui_name() -> &'static str {
"SelectableExampleView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Stack::new()
.with_child(Rect::new().with_background_color(ColorU::black()).finish())
.with_child(
Flex::column()
.with_child(
SelectableArea::new(
self.selectable_area_state_handle_1.clone(),
|selection_args, _, _| {
println!("SELECTED TEXT - {:?}", selection_args.selection);
},
Container::new(
ConstrainedBox::new(
Flex::row()
.with_child(
Container::new(
Flex::column()
.with_children([
Container::new(
Text::new(
"HELLO WORLD 1",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
ConstrainedBox::new(
Text::new(
"HELLO WORLD 2",
self.font_family,
16.,
)
.finish(),
)
.with_width(400.)
.with_height(400.)
.finish(),
Container::new(
Text::new_inline(
"HELLO WORLD 3",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
])
.finish(),
)
.with_horizontal_margin(10.)
.with_uniform_padding(10.)
.with_border(
Border::all(1.0).with_border_fill(ColorU::new(
255, 194, 255, 255,
)),
)
.finish(),
)
.with_child(
Container::new(
Flex::column()
.with_children([
Container::new(
Text::new_inline(
"HELLO WORLD 4",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
ConstrainedBox::new(
Text::new(
"HELLO WORLD 5",
self.font_family,
16.,
)
.finish(),
)
.finish(),
Container::new(
Text::new_inline(
"HELLO WORLD 6",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
])
.finish(),
)
.with_horizontal_margin(10.)
.with_uniform_padding(10.)
.with_border(
Border::all(1.0).with_border_fill(ColorU::new(
255, 194, 255, 255,
)),
)
.finish(),
)
.finish(),
)
.finish(),
)
.with_uniform_padding(100.)
.finish(),
)
.finish(),
)
.with_child(
SelectableArea::new(
self.selectable_area_state_handle_2.clone(),
|selection_args, _, _| {
println!("SELECTED TEXT - {:?}", selection_args.selection);
},
Container::new(
ConstrainedBox::new(
Flex::row()
.with_child(
Container::new(
Flex::column()
.with_children([
Container::new(
Text::new_inline(
"HELLO WORLD 11",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
ConstrainedBox::new(
Text::new(
"HELLO WORLD 22",
self.font_family,
16.,
)
.finish(),
)
.finish(),
Container::new(
Text::new_inline(
"HELLO WORLD 33",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
])
.finish(),
)
.with_horizontal_margin(10.)
.with_uniform_padding(10.)
.with_border(
Border::all(1.0).with_border_fill(ColorU::new(
255, 194, 255, 255,
)),
)
.finish(),
)
.with_child(
Container::new(
Flex::column()
.with_children([
Container::new(
Text::new_inline(
"HELLO WORLD 44",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
ConstrainedBox::new(
Text::new(
"HELLO 👀👀👀 WORLD 55",
self.font_family,
16.,
)
.finish(),
)
.with_width(400.)
.with_height(400.)
.finish(),
Container::new(
Text::new_inline(
"HELLO WORLD 66 👀",
self.font_family,
16.,
)
.finish(),
)
.with_vertical_margin(10.)
.finish(),
])
.finish(),
)
.with_horizontal_margin(10.)
.with_uniform_padding(10.)
.with_border(
Border::all(1.0).with_border_fill(ColorU::new(
255, 194, 255, 255,
)),
)
.finish(),
)
.finish(),
)
.finish(),
)
.with_uniform_margin(100.)
.finish(),
)
.finish(),
)
.finish(),
)
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
+33
View File
@@ -0,0 +1,33 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(galaxyui::AddWindowOptions::default(), |_| root_view::RootView);
});
Ok(())
}
@@ -0,0 +1,127 @@
use pathfinder_geometry::vector::vec2f;
use galaxyui::elements::{
Align, ConstrainedBox, Container, CornerRadius, DropShadow, Radius, Shrinkable,
};
use galaxyui::{
elements::{Flex, ParentElement, Rect},
AppContext, Element, Entity, TypedActionView, View,
};
use galaxyui::color::ColorU;
pub struct RootView;
impl Entity for RootView {
type Event = ();
}
fn rect_with_shadow(shadow: DropShadow, corner_radius: CornerRadius) -> Box<dyn Element> {
Shrinkable::new(
1.,
Container::new(
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(255, 255, 255, 255))
.with_corner_radius(corner_radius)
.with_drop_shadow(shadow)
.finish(),
)
.with_width(200.)
.with_height(100.)
.finish(),
)
.with_uniform_margin(30.)
.finish(),
)
.finish()
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Container::new(
Align::new(
Flex::column()
.with_children([
rect_with_shadow(
DropShadow {
color: ColorU::black(),
offset: vec2f(0., 10.),
blur_radius: 10.,
spread_radius: 30.,
},
CornerRadius::default(),
),
rect_with_shadow(
DropShadow {
color: ColorU::new(255, 0, 0, 255),
offset: vec2f(10., 10.),
blur_radius: 5.,
spread_radius: 20.,
},
CornerRadius::with_all(Radius::Pixels(8.)),
),
rect_with_shadow(
DropShadow {
color: ColorU::new(0, 255, 0, 255),
offset: vec2f(-10., -20.),
blur_radius: 20.,
spread_radius: 10.,
},
CornerRadius::with_all(Radius::Percentage(30.)),
),
rect_with_shadow(
DropShadow {
color: ColorU::new(0, 0, 255, 255),
offset: vec2f(30., 0.),
blur_radius: 30.,
spread_radius: 40.,
},
CornerRadius::with_right(Radius::Pixels(40.)),
),
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::white())
.with_corner_radius(CornerRadius::with_all(Radius::Percentage(50.)))
.with_drop_shadow(DropShadow {
color: ColorU::black(),
offset: vec2f(-0.5, 2.),
blur_radius: 20.,
spread_radius: 0.,
})
.finish(),
)
.with_width(30.)
.with_height(30.)
.finish(),
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::white())
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(10.)))
.with_drop_shadow(DropShadow {
color: ColorU::black(),
offset: vec2f(50.5, 2.),
blur_radius: 2.,
spread_radius: 0.,
})
.finish(),
)
.with_width(30.)
.with_height(30.)
.finish(),
])
.finish(),
)
.finish(),
)
.with_background_color(ColorU::new(128, 128, 128, 255))
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
+36
View File
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,110 @@
use pathfinder_color::ColorU;
use galaxyui::{
elements::{Align, Container},
presenter::ChildView,
ui_components::{
components::{UiComponent, UiComponentStyles},
slider::{Slider, SliderStateHandle},
},
AppContext, Element, Entity, TypedActionView, View, ViewContext, ViewHandle,
};
/// Renders a center-aligned slider component against a black background. When the slider is
/// dragged, the updated value is printed to stdout.
#[derive(Default)]
pub struct SliderExample {
slider_state: SliderStateHandle,
}
impl SliderExample {
pub fn new() -> Self {
Self {
slider_state: Default::default(),
}
}
}
impl View for SliderExample {
fn ui_name() -> &'static str {
"SliderExample"
}
fn render(&self, _: &AppContext) -> Box<dyn Element> {
Slider::new(self.slider_state.clone())
.on_drag(|event_ctx, _app, new_value| {
event_ctx.dispatch_typed_action(SliderExampleAction::OnSliderDrag(new_value))
})
.on_change(|event_ctx, _app, new_value| {
event_ctx.dispatch_typed_action(SliderExampleAction::OnSliderValueChange(new_value))
})
// Set a custom value range.
.with_range(0.0..100.)
.with_style(UiComponentStyles {
width: Some(400.),
..Default::default()
})
.build()
.finish()
}
}
impl Entity for SliderExample {
type Event = ();
}
#[derive(Debug)]
pub enum SliderExampleAction {
OnSliderDrag(f32),
OnSliderValueChange(f32),
}
impl TypedActionView for SliderExample {
type Action = SliderExampleAction;
fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
match action {
SliderExampleAction::OnSliderDrag(new_value) => {
println!("Slider dragged: {new_value:?}");
ctx.notify();
}
SliderExampleAction::OnSliderValueChange(new_value) => {
println!("Slider dropped: {new_value:?}");
ctx.notify();
}
}
}
}
/// Create a wrapper view so [`SliderExample`] can be added as a [`TypedActionView`].
pub struct RootView {
slider_example_view: ViewHandle<SliderExample>,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
let slider_example_view = ctx.add_typed_action_view(|_| SliderExample::new());
Self {
slider_example_view,
}
}
}
impl Entity for RootView {
type Event = ();
}
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
fn render(&self, _app: &AppContext) -> Box<dyn Element> {
Container::new(Align::new(ChildView::new(&self.slider_example_view).finish()).finish())
.with_background_color(ColorU::black())
.finish()
}
}
impl TypedActionView for RootView {
type Action = ();
}
@@ -0,0 +1,3 @@
# Don't include output or screenshot directories from testing.
output/
screenshots/
@@ -0,0 +1,25 @@
# WARP.md
## Visual Sanity Check for Table Example Screenshots
This project can auto-generate screenshots of the table example demos and then sanity check them using computer vision. The goal is to quickly catch apparent rendering bugs (e.g., empty cells, obvious misalignment, missing headers) before committing or opening a PR.
### How to capture images
- Build and run the example with capture flags:
- Baseline (reference images): `../../../../target/debug/examples/table-sample --capture-baseline`
- Current (to compare locally): `../../../../target/debug/examples/table-sample --capture-screenshots`
- Output directories:
- Baseline: `screenshots/baseline/`
- Current: `screenshots/current/`
### Sanity-check protocol (Agent/Agent Mode)
- Use the read_file tool to upload all PNGs in the chosen directory (baseline or current).
- For each image, scan for:
- Completely blank/black/solid-color large areas where UI should be rendered
- Obvious missing headers, rows, or columns
- Clearly misaligned row bands or headers vs. body
- Text clipped mid-line or unreadable due to extreme contrast issues
- Report any images that exhibit the above, with a short note.
Notes:
- This is a quick visual smoke test, not a pixel-perfect comparison.
- If a failure is found, re-run the example for a single demo by navigating with arrow keys or by re-running the full capture and re-checking.
@@ -0,0 +1,95 @@
use anyhow::{anyhow, Result};
use pathfinder_geometry::vector::vec2f;
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, platform::WindowBounds, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
pub static ASSETS: Assets = Assets;
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
#[derive(Debug, Clone, Default)]
pub struct CaptureConfig {
pub capture_screenshots: bool,
pub capture_baseline: bool,
}
fn parse_args() -> CaptureConfig {
let args: Vec<String> = std::env::args().collect();
let mut config = CaptureConfig::default();
for arg in args.iter() {
match arg.as_str() {
"--capture-screenshots" => config.capture_screenshots = true,
"--capture-baseline" => {
config.capture_screenshots = true;
config.capture_baseline = true;
}
"--help" | "-h" => {
println!("Table Sample Example - Screenshot Testing");
println!("\nUsage: table-sample [OPTIONS]");
println!("\nOptions:");
println!(" --capture-screenshots Capture screenshots of all demos");
println!(" --capture-baseline Capture and save as baseline screenshots");
println!(" --help, -h Show this help message");
std::process::exit(0);
}
_ => {}
}
}
config
}
fn main() -> Result<()> {
env_logger::builder().format_timestamp_millis().init();
let capture_config = parse_args();
if capture_config.capture_screenshots {
println!("📸 Screenshot capture mode enabled");
if capture_config.capture_baseline {
println!("📁 Baseline mode: screenshots will be saved as reference images");
}
}
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
root_view::init(ctx);
let window_options = galaxyui::AddWindowOptions {
window_bounds: WindowBounds::ExactSize(vec2f(1000.0, 800.0)),
window_style: if capture_config.capture_screenshots {
galaxyui::platform::WindowStyle::NotStealFocus
} else {
galaxyui::platform::WindowStyle::Normal
},
..Default::default()
};
let config = capture_config.clone();
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
let (window_id, _root) = ctx.add_window(window_options, move |view_ctx| {
root_view::RootView::new(view_ctx, config)
});
#[cfg(target_os = "macos")]
if capture_config.capture_screenshots {
// Make it visible for rendering but keep z-index
ctx.windows()
.show_window_and_focus_app_without_ordering_front(window_id);
}
});
Ok(())
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
use anyhow::{anyhow, Result};
use std::borrow::Cow;
pub mod root_view;
extern crate galaxyui;
use rust_embed::RustEmbed;
use galaxyui::{platform, AssetProvider};
#[derive(Clone, Copy, RustEmbed)]
#[folder = "examples/assets"]
pub struct Assets;
// The static assets we need to load in app.
pub static ASSETS: Assets = Assets;
// Implement the AssetProvider trait here (required by App::new).
impl AssetProvider for Assets {
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
<Assets as RustEmbed>::get(path)
.map(|f| f.data)
.ok_or_else(|| anyhow!("no asset exists at path {}", path))
}
}
// Create a view where we could use keybindings to change its state.
// In this example, we are going to have two simple bindings:
// 1. Cmd-Enter to toggle showing / hiding a solid red rect
// 2. Enter to toggle showing / hiding some texts
fn main() -> Result<()> {
let app_builder =
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
let _ = app_builder.run(move |ctx| {
root_view::init(ctx);
ctx.add_window(
galaxyui::AddWindowOptions::default(),
root_view::RootView::new,
);
});
Ok(())
}
@@ -0,0 +1,154 @@
use pathfinder_color::ColorU;
use galaxyui::fonts::FamilyId;
use galaxyui::SingletonEntity as _;
use galaxyui::{
elements::{Align, ConstrainedBox, ParentElement, Rect, Stack, Text},
keymap::FixedBinding,
presenter::ChildView,
AppContext, Element, Entity, TypedActionView, View, ViewContext, ViewHandle,
};
// We could initiate global action and bindings here.
pub fn init(ctx: &mut AppContext) {
use galaxyui::keymap::macros::*;
// Add bindings to trigger actions in the subview.
ctx.register_fixed_bindings([
FixedBinding::new(
"cmdorctrl-enter",
SubViewAction::ToggleRedRect,
id!("SubView"),
),
FixedBinding::new("enter", SubViewAction::ToggleText, id!("SubView")),
]);
}
pub struct RootView {
// RootView "owns" a viewhandle to the subview.
sub_view: ViewHandle<SubView>,
}
impl RootView {
pub fn new(ctx: &mut ViewContext<Self>) -> Self {
// Adding typed action view allows the view to receive keydown events.
let sub_view = ctx.add_typed_action_view(|ctx| {
let menlo = galaxyui::fonts::Cache::handle(ctx).update(ctx, |cache, _| {
cache.load_system_font("Menlo").expect("Should load Menlo")
});
let view = SubView {
display_red_rect: true,
display_text: true,
menlo_font_family: menlo,
};
// Need the view to be focused for keydown actions to be dispatched to it.
ctx.focus_self();
view
});
Self { sub_view }
}
}
// Implement the entity trait.
impl Entity for RootView {
type Event = ();
}
// Implement the view trait so RootView could be considered as a view.
impl View for RootView {
fn ui_name() -> &'static str {
"RootView"
}
// Renders the child view of sub_view.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
ChildView::new(&self.sub_view).finish()
}
}
#[derive(Debug, Clone)]
pub enum SubViewAction {
ToggleRedRect,
ToggleText,
}
pub struct SubView {
display_red_rect: bool,
display_text: bool,
menlo_font_family: FamilyId,
}
// Implement the entity trait.
impl Entity for SubView {
type Event = ();
}
// Implement the view trait so SubView could be considered as a view.
impl View for SubView {
fn ui_name() -> &'static str {
"SubView"
}
// Renders a stack of a centered solid red box and some text on top.
fn render(&self, _: &AppContext) -> Box<dyn Element> {
// Half transparent black background.
let mut stack = Stack::new().with_child(
Rect::new()
.with_background_color(ColorU::new(0, 0, 0, 150))
.finish(),
);
// If flag is true, display a solid red box.
if self.display_red_rect {
stack.add_child(
Align::new(
ConstrainedBox::new(
Rect::new()
.with_background_color(ColorU::new(255, 0, 0, 255))
.finish(),
)
.with_width(300.)
.with_height(200.)
.finish(),
)
.finish(),
);
}
// If flag is true, display some texts.
if self.display_text {
stack.add_child(
Align::new(
ConstrainedBox::new(
Text::new_inline(
"This is some text for testing",
self.menlo_font_family,
12.,
)
.finish(),
)
.with_width(250.)
.finish(),
)
.finish(),
)
};
stack.finish()
}
}
impl TypedActionView for SubView {
type Action = SubViewAction;
fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext<Self>) {
match action {
SubViewAction::ToggleRedRect => self.display_red_rect = !self.display_red_rect,
SubViewAction::ToggleText => self.display_text = !self.display_text,
};
ctx.notify();
}
}
impl TypedActionView for RootView {
type Action = ();
}