copied over from original version in game repo, full rewrite incomingg
This commit is contained in:
parent
0b4a98c83c
commit
dd4ee9732c
13 changed files with 5722 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
.envrc
|
||||
.direnv
|
||||
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
5087
Cargo.lock
generated
Normal file
5087
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "s10e-bevy-menus"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
bevy = "0.16.1"
|
30
examples/basic.rs
Normal file
30
examples/basic.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use bevy::prelude::*;
|
||||
use s10e_bevy_menus::{
|
||||
MenuItemType,
|
||||
menus::{FakeTrigger, Menu, Menus, SimpleMenus},
|
||||
};
|
||||
|
||||
#[derive(States, Debug, Copy, Clone, PartialEq, Eq, Hash, Default, Reflect)]
|
||||
pub enum CurrentMenu {
|
||||
NotInMenus,
|
||||
#[default]
|
||||
MainMenu,
|
||||
MenuA,
|
||||
MenuB,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let menus = SimpleMenus::new(CurrentMenu::NotInMenus, None).add_menu(
|
||||
CurrentMenu::MainMenu,
|
||||
Menu::new().add_items(&[
|
||||
&MenuItemType::Text("Hello world".to_owned()),
|
||||
&MenuItemType::Button("Meoww".to_owned()),
|
||||
]),
|
||||
);
|
||||
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.init_state::<CurrentMenu>()
|
||||
.add_plugins(menus)
|
||||
.run();
|
||||
}
|
100
flake.lock
generated
Normal file
100
flake.lock
generated
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"nodes": {
|
||||
"fenix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756622179,
|
||||
"narHash": "sha256-K3CimrAcMhdDYkErd3oiWPZNaoyaGZEuvGrFuDPFMZY=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "0abcb15ae6279dcb40a8ae7c1ed980705245cb79",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1756636162,
|
||||
"narHash": "sha256-mBecwgUTWRgClJYqcF+y4O1bY8PQHqeDpB+zsAn+/zA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "37ff64b7108517f8b6ba5705ee5085eac636a249",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"fenix": "fenix",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1756597274,
|
||||
"narHash": "sha256-wfaKRKsEVQDB7pQtAt04vRgFphkVscGRpSx3wG1l50E=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "21614ed2d3279a9aa1f15c88d293e65a98991b30",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rust-lang",
|
||||
"ref": "nightly",
|
||||
"repo": "rust-analyzer",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
66
flake.nix
Normal file
66
flake.nix
Normal file
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
description = "Build a cargo project without extra checks";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
|
||||
fenix = {
|
||||
url = "github:nix-community/fenix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
fenix,
|
||||
flake-utils,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
|
||||
rs-toolchain =
|
||||
with fenix.packages.${system};
|
||||
combine [
|
||||
complete.toolchain
|
||||
];
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell rec {
|
||||
buildInputs = with pkgs; [
|
||||
cargo-watch
|
||||
pkg-config
|
||||
rs-toolchain
|
||||
udev
|
||||
alsa-lib
|
||||
glfw
|
||||
freetype
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-validation-layers
|
||||
vulkan-tools # vulkaninfo
|
||||
shaderc # GLSL to SPIRV compiler - glslc
|
||||
renderdoc # Graphics debugger
|
||||
tracy # Graphics profiler
|
||||
vulkan-tools-lunarg
|
||||
xorg.libX11
|
||||
xorg.libXcursor
|
||||
xorg.libXi
|
||||
xorg.libXrandr # To use the x11 feature
|
||||
libxkbcommon
|
||||
wayland
|
||||
clang
|
||||
llvmPackages.bintools
|
||||
];
|
||||
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
9
src/cleanup.rs
Normal file
9
src/cleanup.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
/// Despawn/cleanup every entity with `T`.
|
||||
/// See the [bevy cheatbook's cleanup example](https://bevy-cheatbook.github.io/patterns/generic-systems.html#example-cleanup).
|
||||
pub fn despawn<T: Component>(mut c: Commands, q: Query<Entity, With<T>>) {
|
||||
for e in q {
|
||||
c.entity(e).despawn();
|
||||
}
|
||||
}
|
59
src/item.rs
Normal file
59
src/item.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use bevy::state::state::States;
|
||||
use types::{ItemPosition, MenuItemType, OnPressAction};
|
||||
|
||||
pub mod types;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct MenuItemInternal<NavState: States> {
|
||||
pub(super) r#type: MenuItemType,
|
||||
pub(super) pos: ItemPosition,
|
||||
pub(super) action: Option<OnPressAction<NavState>>,
|
||||
}
|
||||
|
||||
pub(super) trait IntoMenuItemInternal<NavState: States> {
|
||||
fn item(&self) -> MenuItemInternal<NavState>;
|
||||
}
|
||||
|
||||
impl<S: States, T: ?Sized + MenuItem<S>> IntoMenuItemInternal<S> for T {
|
||||
fn item(&self) -> MenuItemInternal<S> {
|
||||
MenuItemInternal {
|
||||
r#type: self.item_type(),
|
||||
pos: self.pos(),
|
||||
action: self.action(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MenuItem<NavState: States> {
|
||||
fn item_type(&self) -> MenuItemType;
|
||||
fn pos(&self) -> ItemPosition;
|
||||
fn action(&self) -> Option<OnPressAction<NavState>>;
|
||||
}
|
||||
|
||||
impl<NavState: States> MenuItem<NavState> for MenuItemType {
|
||||
fn item_type(&self) -> MenuItemType {
|
||||
self.clone()
|
||||
}
|
||||
|
||||
fn pos(&self) -> ItemPosition {
|
||||
ItemPosition::default()
|
||||
}
|
||||
|
||||
fn action(&self) -> Option<OnPressAction<NavState>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<NavState: States> MenuItem<NavState> for (MenuItemType, OnPressAction<NavState>) {
|
||||
fn item_type(&self) -> MenuItemType {
|
||||
self.0.clone()
|
||||
}
|
||||
|
||||
fn pos(&self) -> ItemPosition {
|
||||
ItemPosition::default()
|
||||
}
|
||||
|
||||
fn action(&self) -> Option<OnPressAction<NavState>> {
|
||||
Some(self.1.clone())
|
||||
}
|
||||
}
|
57
src/item/types.rs
Normal file
57
src/item/types.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use bevy::{
|
||||
ecs::{
|
||||
event::Event,
|
||||
system::{Commands, SystemId},
|
||||
},
|
||||
state::state::{FreelyMutableState, NextState, States},
|
||||
};
|
||||
|
||||
/// positions the item in the menu
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ItemPosition {
|
||||
#[default]
|
||||
MainView,
|
||||
North,
|
||||
NorthWest,
|
||||
West,
|
||||
SouthWest,
|
||||
South,
|
||||
SouthEast,
|
||||
East,
|
||||
NorthEast,
|
||||
}
|
||||
|
||||
// TODO: replace strings with custom "content"?
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MenuItemType {
|
||||
Text(String),
|
||||
Button(String),
|
||||
// Subsection(Vec<MenuItemType>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum OnPressAction<NavState: States> {
|
||||
NavigateTo(NavState),
|
||||
DispatchSystem(SystemId),
|
||||
Multiple(Vec<OnPressAction<NavState>>),
|
||||
Close,
|
||||
}
|
||||
|
||||
impl<S: States + FreelyMutableState> OnPressAction<S> {
|
||||
pub fn run(&self, c: &mut Commands, mut nav_state: &mut NextState<S>, closed_state: &S) {
|
||||
dbg!(self);
|
||||
match self {
|
||||
OnPressAction::NavigateTo(to) => nav_state.set(to.clone()),
|
||||
OnPressAction::DispatchSystem(system_id) => c.run_system(*system_id),
|
||||
OnPressAction::Multiple(actions) => {
|
||||
let mut actions = actions.clone();
|
||||
while let Some(action) = actions.pop() {
|
||||
action.run(c, nav_state, closed_state);
|
||||
}
|
||||
}
|
||||
OnPressAction::Close => nav_state.set(closed_state.clone()),
|
||||
}
|
||||
}
|
||||
}
|
25
src/lib.rs
Normal file
25
src/lib.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
#![allow(unused, reason = "Temporary.")]
|
||||
#![feature(iter_collect_into)]
|
||||
//! goal is a custom ui/menu library that allows simple and declarative menus.
|
||||
//! bevy ui is annoying.
|
||||
//!
|
||||
//! TODOs:
|
||||
//! - [ ] cleanup
|
||||
//! - [ ] trigger game events (trigger or normal eventwriter?)
|
||||
//! - [ ] styling
|
||||
//! - [ ] more components
|
||||
//! - [ ] more shorthands
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use bevy::{platform::collections::HashMap, prelude::*};
|
||||
|
||||
mod item;
|
||||
pub mod plugin;
|
||||
|
||||
mod cleanup;
|
||||
pub mod menus;
|
||||
pub use item::types::*;
|
||||
pub use item::*;
|
||||
|
||||
type ToDo = ();
|
77
src/menus.rs
Normal file
77
src/menus.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use bevy::{platform::collections::HashMap, prelude::*};
|
||||
|
||||
use super::{IntoMenuItemInternal, ItemPosition, MenuItem, MenuItemInternal};
|
||||
|
||||
#[derive(States, Clone, Eq, PartialEq, Hash, Debug, Copy, Default)]
|
||||
pub struct FakeTrigger;
|
||||
|
||||
pub type SimpleMenus<S> = Menus<FakeTrigger, S>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Menus<TriggerState, NavState>
|
||||
where
|
||||
TriggerState: States,
|
||||
NavState: States,
|
||||
{
|
||||
pub(super) start: NavState,
|
||||
pub(super) closed_when: NavState,
|
||||
/// The state needed to trigger this set of menus
|
||||
pub(super) trigger: Option<TriggerState>,
|
||||
pub(super) menus: HashMap<NavState, Menu<NavState>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Menu<ParentNavState: States> {
|
||||
pub(super) menu_items: Vec<MenuItemInternal<ParentNavState>>,
|
||||
}
|
||||
|
||||
impl<TriggerState, NavState> Menus<TriggerState, NavState>
|
||||
where
|
||||
TriggerState: States,
|
||||
NavState: States + Default,
|
||||
{
|
||||
pub fn new(closed_when: NavState, trigger: Option<TriggerState>) -> Self {
|
||||
Self {
|
||||
trigger,
|
||||
closed_when,
|
||||
start: NavState::default(),
|
||||
menus: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, NavState> Menus<T, NavState>
|
||||
where
|
||||
T: States,
|
||||
NavState: States,
|
||||
{
|
||||
pub fn add_menu(mut self, state: NavState, menu: Menu<NavState>) -> Self {
|
||||
self.menus.insert(state, menu);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<ParentNavState: States> Menu<ParentNavState> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
menu_items: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds `items` in the default `ItemPosition::MainView`.
|
||||
pub fn add_items(mut self, items: &[&dyn MenuItem<ParentNavState>]) -> Self {
|
||||
self.add_items_positioned(ItemPosition::MainView, items)
|
||||
}
|
||||
|
||||
pub fn add_items_positioned(
|
||||
mut self,
|
||||
position: ItemPosition,
|
||||
items: &[&dyn MenuItem<ParentNavState>],
|
||||
) -> Self {
|
||||
items
|
||||
.iter()
|
||||
.map(|it| (*it).item())
|
||||
.collect_into(&mut self.menu_items);
|
||||
self
|
||||
}
|
||||
}
|
161
src/plugin.rs
Normal file
161
src/plugin.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use bevy::{
|
||||
ecs::spawn::SpawnIter, platform::collections::HashMap, prelude::*,
|
||||
state::state::FreelyMutableState,
|
||||
};
|
||||
|
||||
use crate::cleanup::despawn;
|
||||
|
||||
use super::{
|
||||
ItemPosition, MenuItemInternal, MenuItemType, OnPressAction,
|
||||
menus::{Menu, Menus},
|
||||
};
|
||||
|
||||
mod components;
|
||||
|
||||
impl<TriggerState, NavState> Plugin for Menus<TriggerState, NavState>
|
||||
where
|
||||
TriggerState: States + Copy,
|
||||
NavState: States + FreelyMutableState + Copy,
|
||||
{
|
||||
fn build(&self, app: &mut App) {
|
||||
app.insert_state(self.start)
|
||||
.insert_resource(self.get_store())
|
||||
.insert_resource(MenusClosedWhen(self.closed_when))
|
||||
.add_observer(build_ui::<NavState>)
|
||||
.add_observer(destroy_ui::<NavState>)
|
||||
// .add_observer(destroy_ui::<NavStates>)
|
||||
.add_systems(Update, (update_ui_trigger::<NavState>,));
|
||||
|
||||
if let Some(trigger) = &self.trigger {
|
||||
app.add_systems(
|
||||
Update,
|
||||
handle_press_actions::<NavState>.run_if(in_state(*trigger)),
|
||||
)
|
||||
.add_systems(OnExit(*trigger), close_menu::<NavState>);
|
||||
} else {
|
||||
app.add_systems(Update, handle_press_actions::<NavState>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
struct DestroyUi<S: States>(PhantomData<S>);
|
||||
#[derive(Event)]
|
||||
struct BuildUi<S: States>(S);
|
||||
|
||||
#[derive(Component)]
|
||||
struct UiParent<S: States>(PhantomData<S>);
|
||||
#[derive(Component)]
|
||||
struct UiComponent;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Action<S: States>(OnPressAction<S>);
|
||||
|
||||
impl<T: States, S: States> Menus<T, S> {
|
||||
fn get_store(&self) -> MenusStore<S> {
|
||||
MenusStore(self.menus.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn close_menu<S: States + FreelyMutableState>(
|
||||
mut c: Commands,
|
||||
mut nav_state: ResMut<NextState<S>>,
|
||||
closed_when: Res<MenusClosedWhen<S>>,
|
||||
) {
|
||||
c.run_system_cached(despawn::<UiParent<S>>);
|
||||
nav_state.set(closed_when.0.clone());
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct MenusStore<S: States>(HashMap<S, Menu<S>>);
|
||||
#[derive(Resource)]
|
||||
struct MenusClosedWhen<S: States>(S);
|
||||
|
||||
fn destroy_ui<S: States>(
|
||||
trigger: Trigger<DestroyUi<S>>,
|
||||
ui_parent: Query<Entity, With<UiParent<S>>>,
|
||||
mut c: Commands,
|
||||
) {
|
||||
trace!("destroy_ui called");
|
||||
if let Ok(parent) = ui_parent.single_inner() {
|
||||
c.entity(parent).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
fn build_ui<S: States>(
|
||||
trigger: Trigger<BuildUi<S>>,
|
||||
ui_parent: Query<Entity, With<UiParent<S>>>,
|
||||
mut c: Commands,
|
||||
menus_data: Res<MenusStore<S>>,
|
||||
) {
|
||||
let e = trigger.event();
|
||||
let Some(menu_structure) = menus_data.0.get(&e.0) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let items = menu_structure
|
||||
.menu_items
|
||||
.clone()
|
||||
.into_iter()
|
||||
// TODO: implement other item positions
|
||||
.filter(|it| ItemPosition::MainView == it.pos);
|
||||
|
||||
c.spawn((
|
||||
UiParent(PhantomData::<S>),
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
flex_direction: FlexDirection::Column,
|
||||
..default()
|
||||
},
|
||||
))
|
||||
.with_children(|parent| {
|
||||
for MenuItemInternal { r#type, action, .. } in items {
|
||||
let mut e = match r#type {
|
||||
MenuItemType::Text(s) => parent.spawn(components::text(s)),
|
||||
MenuItemType::Button(s) => parent.spawn(components::button(s)),
|
||||
};
|
||||
e.insert(UiComponent);
|
||||
|
||||
if let Some(action) = action {
|
||||
e.insert(Action(action));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_press_actions<S: States + FreelyMutableState>(
|
||||
mut c: Commands,
|
||||
mut interaction_query: Query<(&Interaction, &Action<S>), Changed<Interaction>>,
|
||||
mut nav_state: ResMut<NextState<S>>,
|
||||
closed_when: Res<MenusClosedWhen<S>>,
|
||||
) {
|
||||
for (interaction, Action(action)) in interaction_query {
|
||||
if *interaction != Interaction::Pressed {
|
||||
continue;
|
||||
}
|
||||
action.run(&mut c, &mut nav_state, &closed_when.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ui_trigger<S: States + PartialEq>(
|
||||
mut c: Commands,
|
||||
mut trans_reader: EventReader<StateTransitionEvent<S>>,
|
||||
menus_data: Res<MenusStore<S>>,
|
||||
closed_when: Res<MenusClosedWhen<S>>,
|
||||
) {
|
||||
for trans in trans_reader.read() {
|
||||
info!("{trans:?}");
|
||||
|
||||
c.trigger(DestroyUi(PhantomData::<S>));
|
||||
if let Some(to_build) = trans.entered.clone() {
|
||||
if to_build != closed_when.0 {
|
||||
c.trigger(BuildUi(to_build));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
src/plugin/components.rs
Normal file
37
src/plugin/components.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
use crate::OnPressAction;
|
||||
|
||||
use super::Action;
|
||||
|
||||
pub fn text(t: String) -> impl Bundle {
|
||||
(
|
||||
Text::new(t),
|
||||
TextFont {
|
||||
// font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 33.0,
|
||||
..default()
|
||||
},
|
||||
TextColor(Color::srgb(0.9, 0.9, 0.9)),
|
||||
TextShadow::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn button(t: String) -> impl Bundle {
|
||||
(
|
||||
Button,
|
||||
Node {
|
||||
border: UiRect::all(Val::Px(5.0)),
|
||||
// horizontally center child text
|
||||
justify_content: JustifyContent::Center,
|
||||
// vertically center child text
|
||||
align_items: AlignItems::Center,
|
||||
padding: UiRect::all(Val::Px(10.0)),
|
||||
..default()
|
||||
},
|
||||
BorderColor(Color::BLACK),
|
||||
BorderRadius::MAX,
|
||||
BackgroundColor(Color::srgb(0.2, 0.2, 0.2)),
|
||||
children![text(t)],
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue