Compare commits
6 Commits
23451eeb4b
...
a86b57d85c
Author | SHA1 | Date |
---|---|---|
Mark | a86b57d85c | |
Mark | 940ed863df | |
Mark | a49e1f61bd | |
Mark | 847542a8cb | |
Mark | 3d59ffffef | |
Mark | a67b7e89fc |
5
TODO.md
5
TODO.md
|
@ -1,11 +1,11 @@
|
||||||
# Specific projects
|
# Specific projects
|
||||||
|
|
||||||
## Now:
|
## Now:
|
||||||
|
- hide ui element
|
||||||
|
- zoom limits
|
||||||
- clean up content
|
- clean up content
|
||||||
- Clean up state api
|
- Clean up state api
|
||||||
- Clean up & document UI api
|
- Clean up & document UI api
|
||||||
- Persistent variables in ui scripts
|
|
||||||
- Better planet icons
|
|
||||||
- Clean up scripting errors
|
- Clean up scripting errors
|
||||||
- Mouse colliders
|
- Mouse colliders
|
||||||
- Fade sprites and text in scrollbox
|
- Fade sprites and text in scrollbox
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
- fps textbox positioning
|
- fps textbox positioning
|
||||||
|
|
||||||
## Small jobs
|
## Small jobs
|
||||||
|
- Better planet icons in radar
|
||||||
- Clean up sprite content (and content in general)
|
- Clean up sprite content (and content in general)
|
||||||
- Check game version in config
|
- Check game version in config
|
||||||
- Fix window resizing
|
- Fix window resizing
|
||||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 1400f7bb89f1190a11a7371bb23778881073a49f
|
Subproject commit b8ea8c5a04c0216304a81ebf396b7320a709d6ed
|
|
@ -92,7 +92,23 @@ file = "ui/landscape/test.png"
|
||||||
file = "ui/landscape-mask.png"
|
file = "ui/landscape-mask.png"
|
||||||
|
|
||||||
[sprite."ui::outfitbg"]
|
[sprite."ui::outfitbg"]
|
||||||
file = "ui/outfit-bg.png"
|
start_at = "off:top"
|
||||||
|
section.off.top = "stop"
|
||||||
|
section.off.bot = "stop"
|
||||||
|
section.off.timing.duration = 0.5
|
||||||
|
section.off.frames = ["ui/outfitbg.png"]
|
||||||
|
section.hover.top = "stop"
|
||||||
|
section.hover.bot = "stop"
|
||||||
|
section.hover.timing.duration = 0.5
|
||||||
|
section.hover.frames = ["ui/outfitbg-hover.png"]
|
||||||
|
section.selected.top = "stop"
|
||||||
|
section.selected.bot = "stop"
|
||||||
|
section.selected.timing.duration = 0.5
|
||||||
|
section.selected.frames = ["ui/outfitbg-selected.png"]
|
||||||
|
section.hoverselected.top = "stop"
|
||||||
|
section.hoverselected.bot = "stop"
|
||||||
|
section.hoverselected.timing.duration = 0.5
|
||||||
|
section.hoverselected.frames = ["ui/outfitbg-hover-selected.png"]
|
||||||
|
|
||||||
[sprite."ui::outfitterbox"]
|
[sprite."ui::outfitterbox"]
|
||||||
file = "ui/outfitter-box.png"
|
file = "ui/outfitter-box.png"
|
||||||
|
|
|
@ -39,6 +39,8 @@ fn init(state) {
|
||||||
|
|
||||||
let radar_size = 150.0;
|
let radar_size = 150.0;
|
||||||
let radar_range = 4000.0;
|
let radar_range = 4000.0;
|
||||||
|
let hide_range = 0.85;
|
||||||
|
let shrink_distance = 20.0;
|
||||||
|
|
||||||
sprite::add(
|
sprite::add(
|
||||||
"radar",
|
"radar",
|
||||||
|
@ -78,9 +80,9 @@ fn event(state, event) {
|
||||||
if type_of(event) == "PlayerShipStateEvent" {
|
if type_of(event) == "PlayerShipStateEvent" {
|
||||||
if state.player_ship().is_landed() {
|
if state.player_ship().is_landed() {
|
||||||
ui::go_to_scene("landed");
|
ui::go_to_scene("landed");
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if type_of(event) == "ScrollEvent" {
|
if type_of(event) == "ScrollEvent" {
|
||||||
|
@ -106,6 +108,7 @@ fn event(state, event) {
|
||||||
if event.key() == "L" && event.is_down() {
|
if event.key() == "L" && event.is_down() {
|
||||||
return PlayerDirective::Land;
|
return PlayerDirective::Land;
|
||||||
}
|
}
|
||||||
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +124,6 @@ fn step(state) {
|
||||||
|
|
||||||
|
|
||||||
// TODO: share variables with init();
|
// TODO: share variables with init();
|
||||||
let radar_size = 150.0;
|
|
||||||
let radar_range = 4000.0;
|
|
||||||
let hide_range = 0.85;
|
|
||||||
let shrink_distance = 20.0;
|
|
||||||
let p_pos = state.player_ship().get_pos();
|
let p_pos = state.player_ship().get_pos();
|
||||||
|
|
||||||
// Radar arrow
|
// Radar arrow
|
||||||
|
@ -285,8 +284,8 @@ fn step(state) {
|
||||||
|
|
||||||
// Window frame
|
// Window frame
|
||||||
{
|
{
|
||||||
let dx = (((state.camera_zoom() / 2.0) * state.window_aspect()) / radar_range) * (radar_size / 2.0);
|
let dx = (((ui::get_camera_zoom() / 2.0) * state.window_aspect()) / radar_range) * (radar_size / 2.0);
|
||||||
let dy = ((state.camera_zoom() / 2.0) / radar_range) * (radar_size / 2.0);
|
let dy = ((ui::get_camera_zoom() / 2.0) / radar_range) * (radar_size / 2.0);
|
||||||
sprite::set_rect("radar.frame.ne",
|
sprite::set_rect("radar.frame.ne",
|
||||||
Rect(
|
Rect(
|
||||||
(radar_size / 2.0 + 5) - dx,
|
(radar_size / 2.0 + 5) - dx,
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
fn init(state) {
|
fn init(state) {
|
||||||
let player = state.player_ship();
|
|
||||||
|
|
||||||
conf::show_starfield(true);
|
conf::show_starfield(true);
|
||||||
conf::show_phys(false);
|
conf::show_phys(false);
|
||||||
|
|
||||||
|
@ -17,8 +15,8 @@ fn init(state) {
|
||||||
sprite::add(
|
sprite::add(
|
||||||
"landscape",
|
"landscape",
|
||||||
{
|
{
|
||||||
if player.is_landed() {
|
if state.player_ship().is_landed() {
|
||||||
player.landed_on().image();
|
state.player_ship().landed_on().image();
|
||||||
} else {
|
} else {
|
||||||
"";
|
"";
|
||||||
}
|
}
|
||||||
|
@ -40,6 +38,9 @@ fn init(state) {
|
||||||
Anchor::Center
|
Anchor::Center
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
// If this is not set, the button will
|
||||||
|
// not receive events
|
||||||
|
sprite::set_disable_events("frame", true);
|
||||||
|
|
||||||
|
|
||||||
textbox::add(
|
textbox::add(
|
||||||
|
@ -54,8 +55,8 @@ fn init(state) {
|
||||||
textbox::align_center("title");
|
textbox::align_center("title");
|
||||||
textbox::font_serif("title");
|
textbox::font_serif("title");
|
||||||
textbox::weight_bold("title");
|
textbox::weight_bold("title");
|
||||||
if player.is_landed() {
|
if state.player_ship().is_landed() {
|
||||||
textbox::set_text("title", player.landed_on().display_name());
|
textbox::set_text("title", state.player_ship().landed_on().display_name());
|
||||||
}
|
}
|
||||||
|
|
||||||
textbox::add(
|
textbox::add(
|
||||||
|
@ -68,8 +69,8 @@ fn init(state) {
|
||||||
Color(1.0, 1.0, 1.0, 1.0)
|
Color(1.0, 1.0, 1.0, 1.0)
|
||||||
);
|
);
|
||||||
textbox::font_sans("desc");
|
textbox::font_sans("desc");
|
||||||
if player.is_landed() {
|
if state.player_ship().is_landed() {
|
||||||
textbox::set_text("desc", player.landed_on().desc());
|
textbox::set_text("desc", state.player_ship().landed_on().desc());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,19 +84,19 @@ fn event(state, event) {
|
||||||
sprite::jump_to("button", "off:top", 0.1);
|
sprite::jump_to("button", "off:top", 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if type_of(event) == "MouseClickEvent" {
|
if type_of(event) == "MouseClickEvent" {
|
||||||
if !event.is_down() {
|
if !event.is_down() {
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let element = event.element();
|
let element = event.element();
|
||||||
if element == "button" {
|
if element == "button" {
|
||||||
ui::go_to_scene("outfitter");
|
ui::go_to_scene("outfitter");
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -103,7 +104,7 @@ fn event(state, event) {
|
||||||
|
|
||||||
if type_of(event) == "KeyboardEvent" {
|
if type_of(event) == "KeyboardEvent" {
|
||||||
if !event.is_down() {
|
if !event.is_down() {
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.key() == "L" {
|
if event.key() == "L" {
|
||||||
|
@ -112,15 +113,15 @@ fn event(state, event) {
|
||||||
|
|
||||||
if event.key() == "O" {
|
if event.key() == "O" {
|
||||||
ui::go_to_scene("outfitter");
|
ui::go_to_scene("outfitter");
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if type_of(event) == "PlayerShipStateEvent" {
|
if type_of(event) == "PlayerShipStateEvent" {
|
||||||
if !state.player_ship().is_landed() {
|
if !state.player_ship().is_landed() {
|
||||||
ui::go_to_scene("flying");
|
ui::go_to_scene("flying");
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -163,7 +163,6 @@ fn init(state) {
|
||||||
|
|
||||||
|
|
||||||
// width should be calculated as a fraction of screen width
|
// width should be calculated as a fraction of screen width
|
||||||
|
|
||||||
let scrollbox_rect = Rect(
|
let scrollbox_rect = Rect(
|
||||||
222.0, -16.0, 470.0, 480.0,
|
222.0, -16.0, 470.0, 480.0,
|
||||||
Anchor::NorthWest,
|
Anchor::NorthWest,
|
||||||
|
@ -172,68 +171,73 @@ fn init(state) {
|
||||||
|
|
||||||
scrollbox::add("outfit_list", scrollbox_rect);
|
scrollbox::add("outfit_list", scrollbox_rect);
|
||||||
|
|
||||||
let p = state.player_ship();
|
let selected_outfit = false;
|
||||||
if p.is_landed() {
|
|
||||||
let s = "";
|
|
||||||
let x = scrollbox_rect.pos().x() + 45.0;
|
|
||||||
let y = scrollbox_rect.pos().y() - 45.0;
|
|
||||||
for xxx in ["1","2","3"] {
|
|
||||||
for i in p.landed_on().outfitter() {
|
|
||||||
s = s + i.display_name() + "\n";
|
|
||||||
|
|
||||||
let thumb_name = "outfit.thumb." + i.index() + xxx;
|
{
|
||||||
let backg_name = "outfit.backg." + i.index() + xxx;
|
// p cannot be saved in the global scope.
|
||||||
let title_name = "outfit.title." + i.index() + xxx;
|
let p = state.player_ship();
|
||||||
|
if p.is_landed() {
|
||||||
|
let s = "";
|
||||||
|
let x = scrollbox_rect.pos().x() + 45.0;
|
||||||
|
let y = scrollbox_rect.pos().y() - 45.0;
|
||||||
|
for xxx in ["1","2","3"] {
|
||||||
|
for i in p.landed_on().outfitter() {
|
||||||
|
s = s + i.display_name() + "\n";
|
||||||
|
|
||||||
sprite::add(
|
let thumb_name = "outfit.thumb." + i.index() + xxx;
|
||||||
backg_name,
|
let backg_name = "outfit.backg." + i.index() + xxx;
|
||||||
"ui::outfitbg",
|
let title_name = "outfit.title." + i.index() + xxx;
|
||||||
Rect(
|
|
||||||
x, y, 90.0, 90.0,
|
|
||||||
Anchor::Center,
|
|
||||||
Anchor::NorthWest
|
|
||||||
)
|
|
||||||
);
|
|
||||||
sprite::preserve_aspect(backg_name, true);
|
|
||||||
scrollbox::add_element("outfit_list", backg_name);
|
|
||||||
|
|
||||||
sprite::add(
|
sprite::add(
|
||||||
thumb_name,
|
backg_name,
|
||||||
i.thumbnail(),
|
"ui::outfitbg",
|
||||||
Rect(
|
Rect(
|
||||||
x, y, 75.0, 75.0,
|
x, y, 90.0, 90.0,
|
||||||
Anchor::Center,
|
Anchor::Center,
|
||||||
Anchor::NorthWest
|
Anchor::NorthWest
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
sprite::preserve_aspect(thumb_name, true);
|
sprite::preserve_aspect(backg_name, true);
|
||||||
scrollbox::add_element("outfit_list", thumb_name);
|
scrollbox::add_element("outfit_list", backg_name);
|
||||||
|
|
||||||
textbox::add(
|
sprite::add(
|
||||||
title_name,
|
thumb_name,
|
||||||
10.0, 10.0,
|
i.thumbnail(),
|
||||||
Rect(
|
Rect(
|
||||||
x, y - 50.0, 90.0, 10.0,
|
x, y, 75.0, 75.0,
|
||||||
Anchor::Center,
|
Anchor::Center,
|
||||||
Anchor::NorthWest,
|
Anchor::NorthWest
|
||||||
),
|
)
|
||||||
Color(1.0, 1.0, 1.0, 1.0)
|
);
|
||||||
);
|
sprite::preserve_aspect(thumb_name, true);
|
||||||
textbox::font_sans(title_name);
|
scrollbox::add_element("outfit_list", thumb_name);
|
||||||
textbox::align_center(title_name);
|
|
||||||
textbox::set_text(title_name, i.display_name());
|
textbox::add(
|
||||||
scrollbox::add_element("outfit_list", title_name);
|
title_name,
|
||||||
|
10.0, 10.0,
|
||||||
x = x + 120.0;
|
Rect(
|
||||||
if x > (
|
x, y - 50.0, 90.0, 10.0,
|
||||||
scrollbox_rect.pos().x() + scrollbox_rect.dim().x() - 45.0
|
Anchor::Center,
|
||||||
) {
|
Anchor::NorthWest,
|
||||||
x = scrollbox_rect.pos().x() + 45.0;
|
),
|
||||||
y = y - 120.0;
|
Color(1.0, 1.0, 1.0, 1.0)
|
||||||
|
);
|
||||||
|
textbox::font_sans(title_name);
|
||||||
|
textbox::align_center(title_name);
|
||||||
|
textbox::set_text(title_name, i.display_name());
|
||||||
|
scrollbox::add_element("outfit_list", title_name);
|
||||||
|
|
||||||
|
x = x + 120.0;
|
||||||
|
if x > (
|
||||||
|
scrollbox_rect.pos().x() + scrollbox_rect.dim().x() - 45.0
|
||||||
|
) {
|
||||||
|
x = scrollbox_rect.pos().x() + 45.0;
|
||||||
|
y = y - 120.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
textbox::set_text("outfit_stats", s);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
textbox::set_text("outfit_stats", s);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,28 +252,51 @@ fn event(state, event) {
|
||||||
sprite::jump_to("exit_button", "off:top", 0.1);
|
sprite::jump_to("exit_button", "off:top", 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
|
if element.starts_with("outfit.backg.") && element != selected_outfit {
|
||||||
|
if event.is_enter() {
|
||||||
|
sprite::jump_to(element, "hover:top", 0.1);
|
||||||
|
} else {
|
||||||
|
sprite::jump_to(element, "off:top", 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: this occasionally breaks because of sprite ordering.
|
||||||
|
// Clicks go to se_box instead!
|
||||||
if type_of(event) == "MouseClickEvent" {
|
if type_of(event) == "MouseClickEvent" {
|
||||||
if !event.is_down() {
|
if !event.is_down() {
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print(event.element());
|
||||||
|
|
||||||
|
|
||||||
let element = event.element();
|
let element = event.element();
|
||||||
if element == "exit_button" {
|
if element == "exit_button" {
|
||||||
ui::go_to_scene("landed");
|
ui::go_to_scene("landed");
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if element.starts_with("outfit.backg.") && element != selected_outfit {
|
||||||
|
if selected_outfit != false {
|
||||||
|
sprite::jump_to(selected_outfit, "off:top", 0.1);
|
||||||
|
}
|
||||||
|
sprite::jump_to(element, "selected:top", 0.1);
|
||||||
|
selected_outfit = element;
|
||||||
|
return PlayerDirective::None;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if type_of(event) == "PlayerShipStateEvent" {
|
if type_of(event) == "PlayerShipStateEvent" {
|
||||||
if !state.player_ship().is_landed() {
|
if !state.player_ship().is_landed() {
|
||||||
ui::go_to_scene("flying");
|
ui::go_to_scene("flying");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
return;
|
return PlayerDirective::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,6 +12,7 @@ use rhai::ImmutableString;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use smartstring::{LazyCompact, SmartString};
|
use smartstring::{LazyCompact, SmartString};
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
fs::File,
|
fs::File,
|
||||||
|
@ -130,7 +131,20 @@ pub struct ContentIndex(Arc<SmartString<LazyCompact>>);
|
||||||
|
|
||||||
impl From<ImmutableString> for ContentIndex {
|
impl From<ImmutableString> for ContentIndex {
|
||||||
fn from(value: ImmutableString) -> Self {
|
fn from(value: ImmutableString) -> Self {
|
||||||
Self::new(&value)
|
Self(Arc::new(value.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ContentIndex> for ImmutableString {
|
||||||
|
fn from(value: ContentIndex) -> Self {
|
||||||
|
ImmutableString::from(Arc::into_inner(value.0).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ContentIndex> for ImmutableString {
|
||||||
|
fn from(value: &ContentIndex) -> Self {
|
||||||
|
let x: &SmartString<LazyCompact> = value.0.borrow();
|
||||||
|
ImmutableString::from(x.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,21 +123,26 @@ fn try_main() -> Result<()> {
|
||||||
let mut game = game::Game::new(content.clone());
|
let mut game = game::Game::new(content.clone());
|
||||||
let p = game.make_player();
|
let p = game.make_player();
|
||||||
|
|
||||||
let player = Arc::new(PlayerAgent::new(&content, p.0));
|
let mut input = Arc::new(RenderInput {
|
||||||
let mut phys_img = Arc::new(PhysImage::new());
|
current_time: game.get_current_time(),
|
||||||
|
ct: content.clone(),
|
||||||
|
phys_img: PhysImage::new(),
|
||||||
|
player: PlayerAgent::new(&content, p.0),
|
||||||
|
// TODO: this is a hack for testing.
|
||||||
|
current_system: content.systems.values().next().unwrap().clone(),
|
||||||
|
timing: game.get_timing().clone(),
|
||||||
|
});
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
{
|
||||||
|
let i = Arc::get_mut(&mut input).unwrap();
|
||||||
|
i.current_time = game.get_current_time();
|
||||||
|
i.timing = game.get_timing().clone();
|
||||||
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::RedrawRequested(window_id) if window_id == gpu.window().id() => {
|
Event::RedrawRequested(window_id) if window_id == gpu.window().id() => {
|
||||||
match gpu.render(RenderInput {
|
match gpu.render(&input) {
|
||||||
current_time: game.get_current_time(),
|
|
||||||
ct: content.clone(),
|
|
||||||
phys_img: phys_img.clone(),
|
|
||||||
player: player.clone(),
|
|
||||||
// TODO: this is a hack for testing.
|
|
||||||
current_system: content.systems.values().next().unwrap().clone(),
|
|
||||||
timing: game.get_timing().clone(),
|
|
||||||
}) {
|
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(wgpu::SurfaceError::Lost) => gpu.resize(&content),
|
Err(wgpu::SurfaceError::Lost) => gpu.resize(&content),
|
||||||
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
|
Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
|
||||||
|
@ -147,8 +152,9 @@ fn try_main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::MainEventsCleared => {
|
Event::MainEventsCleared => {
|
||||||
game.step(&phys_img);
|
let i = Arc::get_mut(&mut input).unwrap();
|
||||||
game.update_image(Arc::get_mut(&mut phys_img).unwrap());
|
game.step(&i.phys_img);
|
||||||
|
game.update_image(&mut i.phys_img);
|
||||||
gpu.window().request_redraw();
|
gpu.window().request_redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,37 +177,20 @@ fn try_main() -> Result<()> {
|
||||||
} => {
|
} => {
|
||||||
let directive = gpu
|
let directive = gpu
|
||||||
.process_input(
|
.process_input(
|
||||||
RenderInput {
|
&input,
|
||||||
current_time: game.get_current_time(),
|
|
||||||
ct: content.clone(),
|
|
||||||
phys_img: phys_img.clone(),
|
|
||||||
player: player.clone(),
|
|
||||||
current_system: content.systems.values().next().unwrap().clone(),
|
|
||||||
timing: game.get_timing().clone(),
|
|
||||||
},
|
|
||||||
InputEvent::Keyboard {
|
InputEvent::Keyboard {
|
||||||
down: state == &ElementState::Pressed,
|
down: state == &ElementState::Pressed,
|
||||||
key: *key,
|
key: *key,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
game.apply_directive(directive, &player);
|
game.apply_directive(directive, &input.player);
|
||||||
}
|
}
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
let directive = gpu
|
let directive = gpu
|
||||||
.process_input(
|
.process_input(&input, InputEvent::MouseMove(position.cast()))
|
||||||
RenderInput {
|
|
||||||
current_time: game.get_current_time(),
|
|
||||||
ct: content.clone(),
|
|
||||||
phys_img: phys_img.clone(),
|
|
||||||
player: player.clone(),
|
|
||||||
current_system: content.systems.values().next().unwrap().clone(),
|
|
||||||
timing: game.get_timing().clone(),
|
|
||||||
},
|
|
||||||
InputEvent::MouseMove(position.cast()),
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
game.apply_directive(directive, &player);
|
game.apply_directive(directive, &input.player);
|
||||||
}
|
}
|
||||||
WindowEvent::MouseInput { state, button, .. } => {
|
WindowEvent::MouseInput { state, button, .. } => {
|
||||||
let down = state == &ElementState::Pressed;
|
let down = state == &ElementState::Pressed;
|
||||||
|
@ -211,45 +200,21 @@ fn try_main() -> Result<()> {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(event) = event {
|
if let Some(event) = event {
|
||||||
let directive = gpu
|
let directive = gpu.process_input(&input, event).unwrap();
|
||||||
.process_input(
|
game.apply_directive(directive, &input.player);
|
||||||
RenderInput {
|
|
||||||
current_time: game.get_current_time(),
|
|
||||||
ct: content.clone(),
|
|
||||||
phys_img: phys_img.clone(),
|
|
||||||
player: player.clone(),
|
|
||||||
current_system: content
|
|
||||||
.systems
|
|
||||||
.values()
|
|
||||||
.next()
|
|
||||||
.unwrap()
|
|
||||||
.clone(),
|
|
||||||
timing: game.get_timing().clone(),
|
|
||||||
},
|
|
||||||
event,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
game.apply_directive(directive, &player);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::MouseWheel { delta, .. } => {
|
WindowEvent::MouseWheel { delta, .. } => {
|
||||||
let directive = gpu
|
let directive = gpu
|
||||||
.process_input(
|
.process_input(
|
||||||
RenderInput {
|
&input,
|
||||||
current_time: game.get_current_time(),
|
|
||||||
ct: content.clone(),
|
|
||||||
phys_img: phys_img.clone(),
|
|
||||||
player: player.clone(),
|
|
||||||
current_system: content.systems.values().next().unwrap().clone(),
|
|
||||||
timing: game.get_timing().clone(),
|
|
||||||
},
|
|
||||||
InputEvent::Scroll(match delta {
|
InputEvent::Scroll(match delta {
|
||||||
MouseScrollDelta::LineDelta(_h, v) => *v,
|
MouseScrollDelta::LineDelta(_h, v) => *v,
|
||||||
MouseScrollDelta::PixelDelta(v) => v.x as f32,
|
MouseScrollDelta::PixelDelta(v) => v.x as f32,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
game.apply_directive(directive, &player);
|
game.apply_directive(directive, &input.player);
|
||||||
}
|
}
|
||||||
WindowEvent::Resized(_) => {
|
WindowEvent::Resized(_) => {
|
||||||
gpu.resize(&content);
|
gpu.resize(&content);
|
||||||
|
|
|
@ -279,17 +279,14 @@ impl GPUState {
|
||||||
/// Handle user input
|
/// Handle user input
|
||||||
pub fn process_input(
|
pub fn process_input(
|
||||||
&mut self,
|
&mut self,
|
||||||
input: RenderInput,
|
input: &Arc<RenderInput>,
|
||||||
event: InputEvent,
|
event: InputEvent,
|
||||||
) -> Result<PlayerDirective> {
|
) -> Result<PlayerDirective> {
|
||||||
let input = Arc::new(input);
|
|
||||||
self.ui.process_input(&mut self.state, input, event)
|
self.ui.process_input(&mut self.state, input, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Main render function. Draws sprites on a window.
|
/// Main render function. Draws sprites on a window.
|
||||||
pub fn render(&mut self, input: RenderInput) -> Result<(), wgpu::SurfaceError> {
|
pub fn render(&mut self, input: &Arc<RenderInput>) -> Result<(), wgpu::SurfaceError> {
|
||||||
let input = Arc::new(input);
|
|
||||||
|
|
||||||
if let Some(ship) = input.player.ship {
|
if let Some(ship) = input.player.ship {
|
||||||
let o = input.phys_img.get_ship(&PhysSimShipHandle(ship));
|
let o = input.phys_img.get_ship(&PhysSimShipHandle(ship));
|
||||||
if let Some(o) = o {
|
if let Some(o) = o {
|
||||||
|
@ -390,7 +387,7 @@ impl GPUState {
|
||||||
self.push_effects(&input, (clip_ne, clip_sw));
|
self.push_effects(&input, (clip_ne, clip_sw));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ui.draw(&mut self.state, input.clone()).unwrap();
|
self.ui.draw(&mut self.state, input).unwrap();
|
||||||
|
|
||||||
// These should match the indices in each shader,
|
// These should match the indices in each shader,
|
||||||
// and should each have a corresponding bind group layout.
|
// and should each have a corresponding bind group layout.
|
||||||
|
|
|
@ -9,13 +9,13 @@ use galactica_util::timing::Timing;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RenderInput {
|
pub struct RenderInput {
|
||||||
/// Player ship data
|
/// Player ship data
|
||||||
pub player: Arc<PlayerAgent>,
|
pub player: PlayerAgent,
|
||||||
|
|
||||||
/// The system we're currently in
|
/// The system we're currently in
|
||||||
pub current_system: Arc<System>,
|
pub current_system: Arc<System>,
|
||||||
|
|
||||||
/// The world state to render
|
/// The world state to render
|
||||||
pub phys_img: Arc<PhysImage>,
|
pub phys_img: PhysImage,
|
||||||
|
|
||||||
// TODO: handle overflow. is it a problem?
|
// TODO: handle overflow. is it a problem?
|
||||||
/// The current time, in seconds
|
/// The current time, in seconds
|
||||||
|
|
|
@ -14,11 +14,10 @@ pub fn build_radialbar_module(state_src: Rc<RefCell<UiState>>) -> Module {
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(
|
.set_into_module(
|
||||||
&mut module,
|
&mut module,
|
||||||
// TODO: fix ugly spaces
|
|
||||||
move |name: ImmutableString, stroke: f32, color: Color, rect: Rect| {
|
move |name: ImmutableString, stroke: f32, color: Color, rect: Rect| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
|
|
||||||
if ui_state.elements.contains_key(&name) {
|
if ui_state.contains_name(&name) {
|
||||||
error!("tried to make a radialbar using an existing name `{name}`");
|
error!("tried to make a radialbar using an existing name `{name}`");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -38,12 +37,23 @@ pub fn build_radialbar_module(state_src: Rc<RefCell<UiState>>) -> Module {
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString, val: f32| {
|
.set_into_module(&mut module, move |name: ImmutableString, val: f32| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::RadialBar(x)) => x.set_val(val),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::RadialBar(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `radialbar::set_val` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::RadialBar(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `radialbar_set_val` on an invalid name `{name}`")
|
error!("called `radialbar::set_val` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_val(val);
|
||||||
});
|
});
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub fn build_scrollbox_module(state_src: Rc<RefCell<UiState>>) -> Module {
|
||||||
.set_into_module(&mut module, move |name: ImmutableString, rect: Rect| {
|
.set_into_module(&mut module, move |name: ImmutableString, rect: Rect| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
|
|
||||||
if ui_state.elements.contains_key(&name) {
|
if ui_state.contains_name(&name) {
|
||||||
error!("tried to make a scrollbox using an existing name `{name}`");
|
error!("tried to make a scrollbox using an existing name `{name}`");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -30,44 +30,41 @@ pub fn build_scrollbox_module(state_src: Rc<RefCell<UiState>>) -> Module {
|
||||||
&mut module,
|
&mut module,
|
||||||
move |name: ImmutableString, target: ImmutableString| {
|
move |name: ImmutableString, target: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
|
|
||||||
|
// Make sure `name` is a scrollbox
|
||||||
match ui_state.get_mut_by_name(&name) {
|
match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Scrollbox(_)) => {
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
match ui_state.get_mut_by_name(&target) {
|
UiElement::Scrollbox(_) => {}
|
||||||
Some(UiElement::Text(_)) | Some(UiElement::Sprite(_)) => {
|
_ => {
|
||||||
let e = match ui_state.remove_element_incomplete(&target) {
|
error!("called `scrollbox::add_element` on an invalid name `{name}`");
|
||||||
Some(UiElement::Sprite(s)) => {
|
return;
|
||||||
Rc::new(RefCell::new(UiElement::Sprite(s)))
|
|
||||||
}
|
|
||||||
Some(UiElement::Text(t)) => {
|
|
||||||
Rc::new(RefCell::new(UiElement::Text(t)))
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add a subelement pointing to this sprite
|
|
||||||
ui_state.add_element(UiElement::SubElement {
|
|
||||||
parent: name.clone(),
|
|
||||||
element: e.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add this sprite to a scrollbox
|
|
||||||
match ui_state.get_mut_by_name(&name) {
|
|
||||||
Some(UiElement::Scrollbox(s)) => {
|
|
||||||
s.add_element(e);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Some(_) => {
|
|
||||||
error!("cannot add `{name}` to scrollbox `{name}`, invalid type.")
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
error!("called `scrollbox::add_element` with a non-existing target `{target}`")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Scrollbox(_)) => {}
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `scrollbox::add_element` on an invalid name `{name}`")
|
error!("called `scrollbox::add_element` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the target with a SubElement, without changing its position in the array.
|
||||||
|
match ui_state.get_mut_by_name(&target) {
|
||||||
|
Some(UiElement::Text(_)) | Some(UiElement::Sprite(_)) => {
|
||||||
|
let e = ui_state.get_mut_by_name(&target).unwrap();
|
||||||
|
let new = (*e).clone();
|
||||||
|
*e = UiElement::SubElement {
|
||||||
|
parent: name.clone(),
|
||||||
|
element: Box::new(new),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
error!("cannot add `{name}` to scrollbox `{name}`, invalid type.")
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!(
|
||||||
|
"called `scrollbox::add_element` with a non-existing target `{target}`"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,7 +26,7 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ui_state.elements.contains_key(&name) {
|
if ui_state.contains_name(&name) {
|
||||||
error!("tried to make a sprite using an existing name `{name}`");
|
error!("tried to make a sprite using an existing name `{name}`");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
if ui_state.elements.contains_key(&name) {
|
if ui_state.contains_name(&name) {
|
||||||
ui_state.remove_element(&name);
|
ui_state.remove_element(&name);
|
||||||
} else {
|
} else {
|
||||||
error!("called `sprite::remove` on an invalid name `{name}`")
|
error!("called `sprite::remove` on an invalid name `{name}`")
|
||||||
|
@ -96,29 +96,36 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
||||||
move |name: ImmutableString, edge_name: ImmutableString, duration: f32| {
|
move |name: ImmutableString, edge_name: ImmutableString, duration: f32| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
|
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Sprite(x)) => {
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
let sprite = x.anim.get_sprite();
|
UiElement::Sprite(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `sprite::jump_to` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
let edge =
|
Some(UiElement::Sprite(x)) => x,
|
||||||
resolve_edge_as_edge(&sprite.sections, edge_name.as_str(), duration);
|
|
||||||
let edge = match edge {
|
|
||||||
Err(_) => {
|
|
||||||
error!(
|
|
||||||
"called `sprite::jump_to` on an invalid edge `{}` on sprite `{}`",
|
|
||||||
edge_name, sprite.index
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Ok(s) => s,
|
|
||||||
};
|
|
||||||
|
|
||||||
x.anim.jump_to(&edge);
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `sprite::jump_to` on an invalid name `{name}`")
|
error!("called `sprite::jump_to` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let sprite = e.anim.get_sprite();
|
||||||
|
let edge = resolve_edge_as_edge(&sprite.sections, edge_name.as_str(), duration);
|
||||||
|
let edge = match edge {
|
||||||
|
Err(_) => {
|
||||||
|
error!(
|
||||||
|
"called `sprite::jump_to` on an invalid edge `{}` on sprite `{}`",
|
||||||
|
edge_name, sprite.index
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ok(s) => s,
|
||||||
|
};
|
||||||
|
|
||||||
|
e.anim.jump_to(&edge);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -127,12 +134,22 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString, x: f32| {
|
.set_into_module(&mut module, move |name: ImmutableString, x: f32| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Sprite(s)) => s.set_angle(x),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Sprite(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `sprite::set_angle` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(UiElement::Sprite(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `sprite::set_angle` on an invalid name `{name}`")
|
error!("called `sprite::set_angle` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_angle(x)
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -140,12 +157,22 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString, x: Rect| {
|
.set_into_module(&mut module, move |name: ImmutableString, x: Rect| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Sprite(s)) => s.set_rect(x),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Sprite(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `sprite::set_rect` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(UiElement::Sprite(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `sprite::set_rect` on an invalid name `{name}`")
|
error!("called `sprite::set_rect` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_rect(x);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -153,12 +180,22 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString, x: Color| {
|
.set_into_module(&mut module, move |name: ImmutableString, x: Color| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Sprite(s)) => s.set_color(x),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Sprite(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `sprite::set_color` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(UiElement::Sprite(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `sprite::set_color` on an invalid name `{name}`")
|
error!("called `sprite::set_color` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_color(x);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -166,12 +203,46 @@ pub fn build_sprite_module(ct_src: Arc<Content>, state_src: Rc<RefCell<UiState>>
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString, x: bool| {
|
.set_into_module(&mut module, move |name: ImmutableString, x: bool| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Sprite(s)) => s.set_preserve_aspect(x),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Sprite(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `sprite::preserve_aspect` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(UiElement::Sprite(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `sprite::set_preserve_aspect` on an invalid name `{name}`")
|
error!("called `sprite::preserve_aspect` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_preserve_aspect(x);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let state = state_src.clone();
|
||||||
|
let _ = FuncRegistration::new("set_disable_events")
|
||||||
|
.with_namespace(FnNamespace::Internal)
|
||||||
|
.set_into_module(&mut module, move |name: ImmutableString, x: bool| {
|
||||||
|
let mut ui_state = state.borrow_mut();
|
||||||
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Sprite(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `sprite::set_disable_events` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some(UiElement::Sprite(x)) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `sprite::set_disable_events` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
e.set_disable_events(x);
|
||||||
|
});
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub fn build_textbox_module(
|
||||||
color: Color| {
|
color: Color| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
|
|
||||||
if ui_state.elements.contains_key(&name) {
|
if ui_state.contains_name(&name) {
|
||||||
error!("tried to make a textbox using an existing name `{name}`");
|
error!("tried to make a textbox using an existing name `{name}`");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -51,12 +51,24 @@ pub fn build_textbox_module(
|
||||||
&mut module,
|
&mut module,
|
||||||
move |name: ImmutableString, text: ImmutableString| {
|
move |name: ImmutableString, text: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
|
||||||
Some(UiElement::Text(x)) => x.set_text(&mut font.borrow_mut(), text.as_str()),
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::set_text` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::set_text` on an invalid name `{name}`")
|
error!("called `textbox::set_text` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_text(&mut font.borrow_mut(), text.as_str());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -66,12 +78,24 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
|
||||||
Some(UiElement::Text(x)) => x.set_align(&mut font.borrow_mut(), Align::Left),
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::align_left` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::align_left` on an invalid name `{name}`")
|
error!("called `textbox::align_left` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_align(&mut font.borrow_mut(), Align::Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -80,12 +104,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_align(&mut font.borrow_mut(), Align::Right),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::align_right` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::align_right` on an invalid name `{name}`")
|
error!("called `textbox::align_right` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_align(&mut font.borrow_mut(), Align::Right);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -94,12 +129,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_align(&mut font.borrow_mut(), Align::Justified),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::align_justify` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::align_justify` on an invalid name `{name}`")
|
error!("called `textbox::align_justify` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_align(&mut font.borrow_mut(), Align::Justified);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -108,12 +154,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_align(&mut font.borrow_mut(), Align::Center),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::align_center` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::align_center` on an invalid name `{name}`")
|
error!("called `textbox::align_center` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_align(&mut font.borrow_mut(), Align::Center);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -122,12 +179,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_weight(&mut font.borrow_mut(), Weight::BOLD),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::weight_bold` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::weight_bold` on an invalid name `{name}`")
|
error!("called `textbox::weight_bold` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_weight(&mut font.borrow_mut(), Weight::BOLD);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -136,12 +204,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_weight(&mut font.borrow_mut(), Weight::NORMAL),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::weight_normal` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::weight_normal` on an invalid name `{name}`")
|
error!("called `textbox::weight_normal` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_weight(&mut font.borrow_mut(), Weight::NORMAL);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -150,12 +229,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_font(&mut font.borrow_mut(), FamilyOwned::Serif),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::font_serif` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::font_serif` on an invalid name `{name}`")
|
error!("called `textbox::font_serif` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_font(&mut font.borrow_mut(), FamilyOwned::Serif);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -164,14 +254,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => {
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
x.set_font(&mut font.borrow_mut(), FamilyOwned::SansSerif)
|
UiElement::Text(x) => x,
|
||||||
}
|
_ => {
|
||||||
|
error!("called `textbox::font_sans` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::font_sans` on an invalid name `{name}`")
|
error!("called `textbox::font_sans` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_font(&mut font.borrow_mut(), FamilyOwned::SansSerif);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -180,14 +279,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => {
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
x.set_font(&mut font.borrow_mut(), FamilyOwned::Monospace)
|
UiElement::Text(x) => x,
|
||||||
}
|
_ => {
|
||||||
|
error!("called `textbox::font_mono` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::font_mono` on an invalid name `{name}`")
|
error!("called `textbox::font_mono` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_font(&mut font.borrow_mut(), FamilyOwned::Monospace);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -196,12 +304,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_style(&mut font.borrow_mut(), Style::Normal),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::style_normal` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::style_normal` on an invalid name `{name}`")
|
error!("called `textbox::style_normal` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_style(&mut font.borrow_mut(), Style::Normal);
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_src.clone();
|
let state = state_src.clone();
|
||||||
|
@ -210,12 +329,23 @@ pub fn build_textbox_module(
|
||||||
.with_namespace(FnNamespace::Internal)
|
.with_namespace(FnNamespace::Internal)
|
||||||
.set_into_module(&mut module, move |name: ImmutableString| {
|
.set_into_module(&mut module, move |name: ImmutableString| {
|
||||||
let mut ui_state = state.borrow_mut();
|
let mut ui_state = state.borrow_mut();
|
||||||
match ui_state.get_mut_by_name(&name) {
|
let e = match ui_state.get_mut_by_name(&name) {
|
||||||
Some(UiElement::Text(x)) => x.set_style(&mut font.borrow_mut(), Style::Italic),
|
Some(UiElement::SubElement { element, .. }) => match &mut **element {
|
||||||
|
UiElement::Text(x) => x,
|
||||||
|
_ => {
|
||||||
|
error!("called `textbox::style_italic` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Some(UiElement::Text(x)) => x,
|
||||||
_ => {
|
_ => {
|
||||||
error!("called `textbox::style_italic` on an invalid name `{name}`")
|
error!("called `textbox::style_italic` on an invalid name `{name}`");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
e.set_style(&mut font.borrow_mut(), Style::Italic);
|
||||||
});
|
});
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub fn register_into_engine(
|
||||||
.build_type::<PlayerShipStateEvent>()
|
.build_type::<PlayerShipStateEvent>()
|
||||||
.build_type::<KeyboardEvent>()
|
.build_type::<KeyboardEvent>()
|
||||||
.build_type::<ScrollEvent>()
|
.build_type::<ScrollEvent>()
|
||||||
// Bigger modules
|
// Enums
|
||||||
.register_type_with_name::<Anchor>("Anchor")
|
.register_type_with_name::<Anchor>("Anchor")
|
||||||
.register_static_module("Anchor", exported_module!(anchor_mod).into())
|
.register_static_module("Anchor", exported_module!(anchor_mod).into())
|
||||||
.register_type_with_name::<PlayerDirective>("PlayerDirective")
|
.register_type_with_name::<PlayerDirective>("PlayerDirective")
|
||||||
|
|
|
@ -241,9 +241,9 @@ unsafe impl Send for State {}
|
||||||
unsafe impl Sync for State {}
|
unsafe impl Sync for State {}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new(state: &RenderState, input: Arc<RenderInput>) -> Self {
|
pub fn new(state: &RenderState, input: &Arc<RenderInput>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
input,
|
input: input.clone(),
|
||||||
window_aspect: state.window_aspect,
|
window_aspect: state.window_aspect,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use nalgebra::Vector2;
|
use nalgebra::Vector2;
|
||||||
use rhai::{Dynamic, ImmutableString};
|
use rhai::{Dynamic, ImmutableString};
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|
||||||
use winit::window::Window;
|
|
||||||
|
|
||||||
use super::{super::api::Rect, OwnedTextArea};
|
use super::super::api::Rect;
|
||||||
use crate::{ui::UiElement, InputEvent, RenderInput, RenderState};
|
use crate::{InputEvent, RenderInput, RenderState};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UiScrollbox {
|
pub struct UiScrollbox {
|
||||||
pub name: ImmutableString,
|
pub name: ImmutableString,
|
||||||
pub rect: Rect,
|
pub rect: Rect,
|
||||||
pub offset: Vector2<f32>,
|
pub offset: Vector2<f32>,
|
||||||
pub elements: HashMap<ImmutableString, Rc<RefCell<UiElement>>>,
|
|
||||||
|
|
||||||
has_mouse: bool,
|
has_mouse: bool,
|
||||||
}
|
}
|
||||||
|
@ -21,31 +18,13 @@ impl UiScrollbox {
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
rect,
|
rect,
|
||||||
elements: HashMap::new(),
|
|
||||||
offset: Vector2::new(0.0, 0.0),
|
offset: Vector2::new(0.0, 0.0),
|
||||||
has_mouse: false,
|
has_mouse: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_element(&mut self, e: Rc<RefCell<UiElement>>) {
|
pub fn step(&mut self, _t: f32) {
|
||||||
let name = e.borrow().get_name().clone();
|
// TODO: inertia
|
||||||
self.elements.insert(name, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_element(&mut self, sprite: &ImmutableString) {
|
|
||||||
self.elements.remove(sprite);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn step(&mut self, t: f32) {
|
|
||||||
for (_name, e) in &self.elements {
|
|
||||||
match &mut *e.clone().borrow_mut() {
|
|
||||||
UiElement::Sprite(sprite) => sprite.step(t),
|
|
||||||
UiElement::RadialBar(_) => {}
|
|
||||||
UiElement::Text(..) => {}
|
|
||||||
UiElement::Scrollbox(..) => {}
|
|
||||||
UiElement::SubElement { .. } => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_event(
|
pub fn handle_event(
|
||||||
|
@ -54,31 +33,21 @@ impl UiScrollbox {
|
||||||
state: &mut RenderState,
|
state: &mut RenderState,
|
||||||
event: &InputEvent,
|
event: &InputEvent,
|
||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
let r = self
|
self.handle_event_with_offset(input, state, event, Vector2::new(0.0, 0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_event_with_offset(
|
||||||
|
&mut self,
|
||||||
|
input: &RenderInput,
|
||||||
|
state: &mut RenderState,
|
||||||
|
event: &InputEvent,
|
||||||
|
offset: Vector2<f32>,
|
||||||
|
) -> Option<Dynamic> {
|
||||||
|
let mut r = self
|
||||||
.rect
|
.rect
|
||||||
.to_centered(&state.window, input.ct.config.ui_scale);
|
.to_centered(&state.window, input.ct.config.ui_scale);
|
||||||
|
r.pos += offset;
|
||||||
|
|
||||||
// TODO: handle only if used in event()
|
|
||||||
// i.e, scrollable sprites shouldn't break scrollboxes
|
|
||||||
// First, check if this event is captured by any sub-elements
|
|
||||||
for (_, e) in &mut self.elements {
|
|
||||||
let arg = match &mut *e.borrow_mut() {
|
|
||||||
UiElement::Sprite(sprite) => sprite.handle_event(&input, state, &event),
|
|
||||||
UiElement::Scrollbox(sbox) => sbox.handle_event(&input, state, &event),
|
|
||||||
|
|
||||||
UiElement::RadialBar(_) | UiElement::Text(..) => None,
|
|
||||||
|
|
||||||
// Subelements are intentionally skipped,
|
|
||||||
// they should be handled by their parent's `handle_event` method.
|
|
||||||
UiElement::SubElement { .. } => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if arg.is_some() {
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no inner events were captured, handle self events.
|
|
||||||
match event {
|
match event {
|
||||||
InputEvent::MouseMove(pos) => {
|
InputEvent::MouseMove(pos) => {
|
||||||
if r.contains_mouse(state, pos) && !self.has_mouse {
|
if r.contains_mouse(state, pos) && !self.has_mouse {
|
||||||
|
@ -102,35 +71,3 @@ impl UiScrollbox {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiScrollbox {
|
|
||||||
pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) {
|
|
||||||
for (_name, e) in &self.elements {
|
|
||||||
match &*e.clone().borrow() {
|
|
||||||
UiElement::Sprite(sprite) => {
|
|
||||||
sprite.push_to_buffer_with_offset(input, state, self.offset)
|
|
||||||
}
|
|
||||||
UiElement::RadialBar(..) => {}
|
|
||||||
UiElement::Text(..) => {}
|
|
||||||
UiElement::Scrollbox(..) => {}
|
|
||||||
UiElement::SubElement { .. } => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: don't allocate here
|
|
||||||
impl<'a> UiScrollbox {
|
|
||||||
pub fn get_textareas(&'a self, input: &RenderInput, window: &Window) -> Vec<OwnedTextArea> {
|
|
||||||
let mut v = Vec::with_capacity(32);
|
|
||||||
for e in self.elements.values() {
|
|
||||||
match &*e.clone().borrow() {
|
|
||||||
UiElement::Text(x) => {
|
|
||||||
v.push(x.get_textarea_with_offset(input, window, self.offset))
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ pub struct UiSprite {
|
||||||
/// Sprite angle, in degrees
|
/// Sprite angle, in degrees
|
||||||
angle: f32,
|
angle: f32,
|
||||||
|
|
||||||
|
/// If true, this sprite ignores all events
|
||||||
|
disable_events: bool,
|
||||||
|
|
||||||
/// If true, this sprite will be scaled to fit in its box without affecting aspect ratio.
|
/// If true, this sprite will be scaled to fit in its box without affecting aspect ratio.
|
||||||
/// If false, this sprite will be stretched to fit in its box
|
/// If false, this sprite will be stretched to fit in its box
|
||||||
preserve_aspect: bool,
|
preserve_aspect: bool,
|
||||||
|
@ -43,6 +46,7 @@ impl UiSprite {
|
||||||
has_mouse: false,
|
has_mouse: false,
|
||||||
has_click: false,
|
has_click: false,
|
||||||
preserve_aspect: false,
|
preserve_aspect: false,
|
||||||
|
disable_events: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +66,10 @@ impl UiSprite {
|
||||||
self.color = color;
|
self.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_disable_events(&mut self, disable_events: bool) {
|
||||||
|
self.disable_events = disable_events;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_preserve_aspect(&mut self, preserve_aspect: bool) {
|
pub fn set_preserve_aspect(&mut self, preserve_aspect: bool) {
|
||||||
self.preserve_aspect = preserve_aspect;
|
self.preserve_aspect = preserve_aspect;
|
||||||
}
|
}
|
||||||
|
@ -124,14 +132,24 @@ impl UiSprite {
|
||||||
state: &mut RenderState,
|
state: &mut RenderState,
|
||||||
event: &InputEvent,
|
event: &InputEvent,
|
||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
let r = self
|
self.handle_event_with_offset(input, state, event, Vector2::new(0.0, 0.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_event_with_offset(
|
||||||
|
&mut self,
|
||||||
|
input: &RenderInput,
|
||||||
|
state: &mut RenderState,
|
||||||
|
event: &InputEvent,
|
||||||
|
offset: Vector2<f32>,
|
||||||
|
) -> Option<Dynamic> {
|
||||||
|
if self.disable_events {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut r = self
|
||||||
.rect
|
.rect
|
||||||
.to_centered(&state.window, input.ct.config.ui_scale);
|
.to_centered(&state.window, input.ct.config.ui_scale);
|
||||||
|
r.pos += offset;
|
||||||
// Release mouse when cursor leaves box
|
|
||||||
if self.has_click && !self.has_mouse {
|
|
||||||
self.has_click = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
InputEvent::MouseMove(pos) => {
|
InputEvent::MouseMove(pos) => {
|
||||||
|
@ -145,6 +163,7 @@ impl UiSprite {
|
||||||
|
|
||||||
if !r.contains_mouse(state, pos) && self.has_mouse {
|
if !r.contains_mouse(state, pos) && self.has_mouse {
|
||||||
self.has_mouse = false;
|
self.has_mouse = false;
|
||||||
|
self.has_click = false;
|
||||||
return Some(Dynamic::from(MouseHoverEvent {
|
return Some(Dynamic::from(MouseHoverEvent {
|
||||||
enter: false,
|
enter: false,
|
||||||
element: self.name.clone(),
|
element: self.name.clone(),
|
||||||
|
|
|
@ -10,7 +10,7 @@ use winit::window::Window;
|
||||||
use super::{super::api::Rect, OwnedTextArea};
|
use super::{super::api::Rect, OwnedTextArea};
|
||||||
use crate::{ui::api, RenderInput};
|
use crate::{ui::api, RenderInput};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct UiTextBox {
|
pub struct UiTextBox {
|
||||||
pub name: ImmutableString,
|
pub name: ImmutableString,
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use galactica_content::Content;
|
||||||
use galactica_system::{phys::PhysSimShipHandle, PlayerDirective};
|
use galactica_system::{phys::PhysSimShipHandle, PlayerDirective};
|
||||||
use galactica_util::rhai_error_to_anyhow;
|
use galactica_util::rhai_error_to_anyhow;
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use rhai::{Dynamic, Engine, ImmutableString, Scope};
|
use rhai::{CallFnOptions, Dynamic, Engine, ImmutableString, Scope};
|
||||||
use std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc};
|
||||||
use winit::event::VirtualKeyCode;
|
use winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
|
@ -58,87 +58,119 @@ impl UiScriptExecutor {
|
||||||
pub fn process_input(
|
pub fn process_input(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut RenderState,
|
state: &mut RenderState,
|
||||||
input: Arc<RenderInput>,
|
input: &Arc<RenderInput>,
|
||||||
event: InputEvent,
|
event: InputEvent,
|
||||||
) -> Result<PlayerDirective> {
|
) -> Result<PlayerDirective> {
|
||||||
let current_scene = (*self.state).borrow().get_scene().clone();
|
let current_scene = (*self.state).borrow().get_scene().clone();
|
||||||
if current_scene.is_none() {
|
if current_scene.is_none() {
|
||||||
return Ok(PlayerDirective::None);
|
return Ok(PlayerDirective::None);
|
||||||
}
|
}
|
||||||
let mut arg: Option<Dynamic> = None;
|
|
||||||
|
|
||||||
// First, check if this event is captured by any ui elements.
|
// First, check if this event is captured by any ui elements.
|
||||||
for (_, e) in &mut self.state.borrow_mut().elements {
|
let len = (*self.state).borrow().len();
|
||||||
arg = match e {
|
// Iterate front to back
|
||||||
|
// (this also ensures that sub-elements are handled BEFORE their container.)
|
||||||
|
for i in (0..len).rev() {
|
||||||
|
let mut ui_state = self.state.borrow_mut();
|
||||||
|
let arg = match ui_state.get_mut_by_idx(i).unwrap() {
|
||||||
UiElement::Sprite(sprite) => sprite.handle_event(&input, state, &event),
|
UiElement::Sprite(sprite) => sprite.handle_event(&input, state, &event),
|
||||||
UiElement::Scrollbox(sbox) => sbox.handle_event(&input, state, &event),
|
UiElement::Scrollbox(sbox) => sbox.handle_event(&input, state, &event),
|
||||||
|
|
||||||
UiElement::RadialBar(_) | UiElement::Text(..) => None,
|
UiElement::RadialBar(_) | UiElement::Text(..) => None,
|
||||||
|
UiElement::SubElement { parent, element } => {
|
||||||
// Subelements are intentionally skipped,
|
// Be very careful here, to avoid
|
||||||
// they should be handled by their parent's `handle_event` method.
|
// borrowing mutably twice...
|
||||||
UiElement::SubElement { .. } => None,
|
let e_name = element.get_name();
|
||||||
|
let p_name = parent.clone();
|
||||||
|
let parent = ui_state.get_by_name(&p_name).unwrap();
|
||||||
|
match parent {
|
||||||
|
UiElement::Scrollbox(sbox) => {
|
||||||
|
let offset = sbox.offset;
|
||||||
|
let element = ui_state.get_mut_by_name(&e_name).unwrap();
|
||||||
|
match element {
|
||||||
|
UiElement::SubElement { element, .. } => match &mut **element {
|
||||||
|
UiElement::Sprite(sprite) => sprite
|
||||||
|
.handle_event_with_offset(&input, state, &event, offset),
|
||||||
|
UiElement::Scrollbox(sbox) => {
|
||||||
|
sbox.handle_event_with_offset(&input, state, &event, offset)
|
||||||
|
}
|
||||||
|
UiElement::RadialBar(_) | UiElement::Text(..) => None,
|
||||||
|
UiElement::SubElement { .. } => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
drop(ui_state);
|
||||||
|
|
||||||
if arg.is_some() {
|
// Return if event hook returns any PlayerDirective (including None),
|
||||||
break;
|
// continue to the next element if the hook returns ().
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
let result = self.run_event_callback(state, &input, arg)?;
|
||||||
|
|
||||||
|
if let Some(result) = result {
|
||||||
|
return Ok(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If nothing was caught, check global events
|
// If nothing was caught, check global events
|
||||||
if arg.is_none() {
|
let arg = match event {
|
||||||
arg = match event {
|
InputEvent::Scroll(val) => Some(Dynamic::from(ScrollEvent { val })),
|
||||||
InputEvent::Scroll(val) => Some(Dynamic::from(ScrollEvent { val })),
|
InputEvent::Keyboard { down, key } => {
|
||||||
InputEvent::Keyboard { down, key } => {
|
let str = match key {
|
||||||
let str = match key {
|
VirtualKeyCode::A => Some("A"),
|
||||||
VirtualKeyCode::A => Some("A"),
|
VirtualKeyCode::B => Some("B"),
|
||||||
VirtualKeyCode::B => Some("B"),
|
VirtualKeyCode::C => Some("C"),
|
||||||
VirtualKeyCode::C => Some("C"),
|
VirtualKeyCode::D => Some("D"),
|
||||||
VirtualKeyCode::D => Some("D"),
|
VirtualKeyCode::E => Some("E"),
|
||||||
VirtualKeyCode::E => Some("E"),
|
VirtualKeyCode::F => Some("F"),
|
||||||
VirtualKeyCode::F => Some("F"),
|
VirtualKeyCode::G => Some("G"),
|
||||||
VirtualKeyCode::G => Some("G"),
|
VirtualKeyCode::H => Some("H"),
|
||||||
VirtualKeyCode::H => Some("H"),
|
VirtualKeyCode::I => Some("I"),
|
||||||
VirtualKeyCode::I => Some("I"),
|
VirtualKeyCode::J => Some("J"),
|
||||||
VirtualKeyCode::J => Some("J"),
|
VirtualKeyCode::K => Some("K"),
|
||||||
VirtualKeyCode::K => Some("K"),
|
VirtualKeyCode::L => Some("L"),
|
||||||
VirtualKeyCode::L => Some("L"),
|
VirtualKeyCode::M => Some("M"),
|
||||||
VirtualKeyCode::M => Some("M"),
|
VirtualKeyCode::N => Some("N"),
|
||||||
VirtualKeyCode::N => Some("N"),
|
VirtualKeyCode::O => Some("O"),
|
||||||
VirtualKeyCode::O => Some("O"),
|
VirtualKeyCode::P => Some("P"),
|
||||||
VirtualKeyCode::P => Some("P"),
|
VirtualKeyCode::Q => Some("Q"),
|
||||||
VirtualKeyCode::Q => Some("Q"),
|
VirtualKeyCode::R => Some("R"),
|
||||||
VirtualKeyCode::R => Some("R"),
|
VirtualKeyCode::S => Some("S"),
|
||||||
VirtualKeyCode::S => Some("S"),
|
VirtualKeyCode::T => Some("T"),
|
||||||
VirtualKeyCode::T => Some("T"),
|
VirtualKeyCode::U => Some("U"),
|
||||||
VirtualKeyCode::U => Some("U"),
|
VirtualKeyCode::V => Some("V"),
|
||||||
VirtualKeyCode::V => Some("V"),
|
VirtualKeyCode::W => Some("W"),
|
||||||
VirtualKeyCode::W => Some("W"),
|
VirtualKeyCode::X => Some("X"),
|
||||||
VirtualKeyCode::X => Some("X"),
|
VirtualKeyCode::Y => Some("Y"),
|
||||||
VirtualKeyCode::Y => Some("Y"),
|
VirtualKeyCode::Z => Some("Z"),
|
||||||
VirtualKeyCode::Z => Some("Z"),
|
VirtualKeyCode::Up => Some("up"),
|
||||||
VirtualKeyCode::Up => Some("up"),
|
VirtualKeyCode::Down => Some("down"),
|
||||||
VirtualKeyCode::Down => Some("down"),
|
VirtualKeyCode::Left => Some("left"),
|
||||||
VirtualKeyCode::Left => Some("left"),
|
VirtualKeyCode::Right => Some("right"),
|
||||||
VirtualKeyCode::Right => Some("right"),
|
VirtualKeyCode::Space => Some("space"),
|
||||||
VirtualKeyCode::Space => Some("space"),
|
_ => None,
|
||||||
_ => None,
|
};
|
||||||
};
|
if let Some(str) = str {
|
||||||
if let Some(str) = str {
|
Some(Dynamic::from(KeyboardEvent {
|
||||||
Some(Dynamic::from(KeyboardEvent {
|
down,
|
||||||
down,
|
key: ImmutableString::from(str),
|
||||||
key: ImmutableString::from(str),
|
}))
|
||||||
}))
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => None,
|
}
|
||||||
};
|
_ => None,
|
||||||
}
|
};
|
||||||
|
|
||||||
if let Some(arg) = arg {
|
if let Some(arg) = arg {
|
||||||
self.run_event_callback(state, input, arg)
|
Ok(self
|
||||||
|
.run_event_callback(state, &input, arg)?
|
||||||
|
.unwrap_or(PlayerDirective::None))
|
||||||
} else {
|
} else {
|
||||||
return Ok(PlayerDirective::None);
|
return Ok(PlayerDirective::None);
|
||||||
}
|
}
|
||||||
|
@ -147,39 +179,42 @@ impl UiScriptExecutor {
|
||||||
fn run_event_callback(
|
fn run_event_callback(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut RenderState,
|
state: &mut RenderState,
|
||||||
input: Arc<RenderInput>,
|
input: &Arc<RenderInput>,
|
||||||
arg: Dynamic,
|
arg: Dynamic,
|
||||||
) -> Result<PlayerDirective> {
|
) -> Result<Option<PlayerDirective>> {
|
||||||
let current_scene = (*self.state).borrow().get_scene().clone();
|
let current_scene = (*self.state).borrow().get_scene().clone();
|
||||||
if current_scene.is_none() {
|
if current_scene.is_none() {
|
||||||
return Ok(PlayerDirective::None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
let current_scene = current_scene.unwrap();
|
let current_scene = current_scene.unwrap();
|
||||||
let ct = (*self.state).borrow().ct.clone();
|
let ct = (*self.state).borrow().ct.clone();
|
||||||
|
let ast = ct.config.ui_scenes.get(current_scene.as_str()).unwrap();
|
||||||
|
|
||||||
let d: Dynamic = rhai_error_to_anyhow(self.engine.call_fn(
|
let d: Dynamic = rhai_error_to_anyhow::<Dynamic, _>(self.engine.call_fn_with_options(
|
||||||
|
CallFnOptions::new().rewind_scope(true),
|
||||||
&mut self.scope,
|
&mut self.scope,
|
||||||
ct.config.ui_scenes.get(current_scene.as_str()).unwrap(),
|
ast,
|
||||||
"event",
|
"event",
|
||||||
(State::new(state, input.clone()), arg.clone()),
|
(State::new(state, input), arg.clone()),
|
||||||
))
|
))
|
||||||
.with_context(|| format!("while handling event `{:?}`", arg))
|
.with_context(|| format!("while calling `event()`"))
|
||||||
.with_context(|| format!("in ui scene `{}`", current_scene))?;
|
.with_context(|| format!("in UI scene `{}`", current_scene.as_str()))?;
|
||||||
|
|
||||||
if d.is::<PlayerDirective>() {
|
if d.is::<PlayerDirective>() {
|
||||||
return Ok(d.cast());
|
return Ok(Some(d.cast::<PlayerDirective>()));
|
||||||
} else if !(d.is_unit()) {
|
} else if !(d.is_unit()) {
|
||||||
error!(
|
error!(
|
||||||
"`event()` in UI scene `{current_scene}` returned invalid type `{}`",
|
"`event()` in UI scene `{}` returned invalid type `{}`",
|
||||||
d
|
d,
|
||||||
|
current_scene.as_str()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(PlayerDirective::None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the current scene
|
/// Change the current scene
|
||||||
pub fn set_scene(&mut self, state: &RenderState, input: Arc<RenderInput>) -> Result<()> {
|
pub fn set_scene(&mut self, state: &RenderState, input: &Arc<RenderInput>) -> Result<()> {
|
||||||
let current_scene = (*self.state).borrow().get_scene().clone();
|
let current_scene = (*self.state).borrow().get_scene().clone();
|
||||||
if self.last_scene == current_scene {
|
if self.last_scene == current_scene {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -195,33 +230,44 @@ impl UiScriptExecutor {
|
||||||
current_scene.as_ref().unwrap()
|
current_scene.as_ref().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
self.scope.clear();
|
|
||||||
|
|
||||||
// Drop this right away, since all script calls borrow elm mutably.
|
|
||||||
let mut elm = self.state.borrow_mut();
|
|
||||||
elm.clear();
|
|
||||||
drop(elm);
|
|
||||||
|
|
||||||
let ct = (*self.state).borrow().ct.clone();
|
let ct = (*self.state).borrow().ct.clone();
|
||||||
rhai_error_to_anyhow(
|
let ast = ct
|
||||||
self.engine.call_fn(
|
.config
|
||||||
&mut self.scope,
|
.ui_scenes
|
||||||
ct.config
|
.get(current_scene.as_ref().unwrap().as_str())
|
||||||
.ui_scenes
|
.unwrap();
|
||||||
.get(current_scene.as_ref().unwrap().as_str())
|
|
||||||
.unwrap(),
|
// Clear previous state
|
||||||
"init",
|
self.scope.clear();
|
||||||
(State::new(state, input.clone()),),
|
self.state.borrow_mut().clear();
|
||||||
),
|
|
||||||
)
|
rhai_error_to_anyhow(self.engine.call_fn_with_options(
|
||||||
|
CallFnOptions::new().rewind_scope(false),
|
||||||
|
&mut self.scope,
|
||||||
|
ast,
|
||||||
|
"init",
|
||||||
|
(State::new(state, input),),
|
||||||
|
))
|
||||||
.with_context(|| format!("while running `init()`"))
|
.with_context(|| format!("while running `init()`"))
|
||||||
.with_context(|| format!("in ui scene `{}`", current_scene.as_ref().unwrap()))?;
|
.with_context(|| format!("in ui scene `{}`", current_scene.as_ref().unwrap()))?;
|
||||||
|
|
||||||
|
// Make sure input isn't borrowed (which would happen if the UI script adds a state class to the scope)
|
||||||
|
// At this point, the only reference to input should be the one in main.rs.
|
||||||
|
// Note how we always pass input as &Arc, not Arc.
|
||||||
|
if Arc::strong_count(&input) != 1 {
|
||||||
|
error!(
|
||||||
|
"State has been captured in the scope of UI scene `{}`",
|
||||||
|
current_scene.as_ref().unwrap()
|
||||||
|
);
|
||||||
|
error!("Clearing scope to prevent a panic, this may break the UI script.");
|
||||||
|
self.scope.clear();
|
||||||
|
};
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw all ui elements
|
/// Draw all ui elements
|
||||||
pub fn draw(&mut self, state: &mut RenderState, input: Arc<RenderInput>) -> Result<()> {
|
pub fn draw(&mut self, state: &mut RenderState, input: &Arc<RenderInput>) -> Result<()> {
|
||||||
let ct = (*self.state).borrow().ct.clone();
|
let ct = (*self.state).borrow().ct.clone();
|
||||||
|
|
||||||
// Initialize start scene if we haven't yet
|
// Initialize start scene if we haven't yet
|
||||||
|
@ -230,10 +276,10 @@ impl UiScriptExecutor {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.set_scene(ImmutableString::from(&ct.config.start_ui_scene));
|
.set_scene(ImmutableString::from(&ct.config.start_ui_scene));
|
||||||
}
|
}
|
||||||
self.set_scene(state, input.clone())?;
|
self.set_scene(state, input)?;
|
||||||
let current_scene = (*self.state).borrow().get_scene().clone();
|
let current_scene = (*self.state).borrow().get_scene().clone();
|
||||||
|
|
||||||
(*self.state).borrow_mut().step(state, input.clone());
|
(*self.state).borrow_mut().step(state, input);
|
||||||
|
|
||||||
// Run step() (if it is defined)
|
// Run step() (if it is defined)
|
||||||
let ast = ct
|
let ast = ct
|
||||||
|
@ -242,11 +288,12 @@ impl UiScriptExecutor {
|
||||||
.get(current_scene.as_ref().unwrap().as_str())
|
.get(current_scene.as_ref().unwrap().as_str())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if ast.iter_functions().any(|x| x.name == "step") {
|
if ast.iter_functions().any(|x| x.name == "step") {
|
||||||
rhai_error_to_anyhow(self.engine.call_fn(
|
rhai_error_to_anyhow(self.engine.call_fn_with_options(
|
||||||
|
CallFnOptions::new().rewind_scope(true),
|
||||||
&mut self.scope,
|
&mut self.scope,
|
||||||
ast,
|
ast,
|
||||||
"step",
|
"step",
|
||||||
(State::new(state, input.clone()),),
|
(State::new(state, input),),
|
||||||
))
|
))
|
||||||
.with_context(|| format!("while calling `step()`"))
|
.with_context(|| format!("while calling `step()`"))
|
||||||
.with_context(|| format!("in ui scene `{}`", current_scene.as_ref().unwrap()))?;
|
.with_context(|| format!("in ui scene `{}`", current_scene.as_ref().unwrap()))?;
|
||||||
|
@ -271,12 +318,12 @@ impl UiScriptExecutor {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
self.run_event_callback(state, input.clone(), Dynamic::from(PlayerShipStateEvent {}))?;
|
self.run_event_callback(state, &input, Dynamic::from(PlayerShipStateEvent {}))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = (*self.state).borrow().len();
|
let len = (*self.state).borrow().len();
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
match (*self.state).borrow_mut().get_mut_by_idx(i).unwrap() {
|
match self.state.borrow().get_by_idx(i).unwrap() {
|
||||||
UiElement::Sprite(sprite) => {
|
UiElement::Sprite(sprite) => {
|
||||||
sprite.push_to_buffer(&input, state);
|
sprite.push_to_buffer(&input, state);
|
||||||
}
|
}
|
||||||
|
@ -285,15 +332,27 @@ impl UiScriptExecutor {
|
||||||
x.push_to_buffer(&input, state);
|
x.push_to_buffer(&input, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
UiElement::Scrollbox(x) => {
|
UiElement::Scrollbox(_) => {}
|
||||||
x.push_to_buffer(&input, state);
|
UiElement::Text(..) => {}
|
||||||
}
|
|
||||||
|
|
||||||
UiElement::SubElement { .. } | UiElement::Text(..) => {}
|
UiElement::SubElement { element, parent } => {
|
||||||
|
match self.state.borrow().get_by_name(parent).unwrap() {
|
||||||
|
UiElement::Scrollbox(sbox) => match &**element {
|
||||||
|
UiElement::Sprite(sprite) => {
|
||||||
|
sprite.push_to_buffer_with_offset(input, state, sbox.offset)
|
||||||
|
}
|
||||||
|
UiElement::RadialBar(..) => {}
|
||||||
|
UiElement::Text(..) => {}
|
||||||
|
UiElement::Scrollbox(..) => {}
|
||||||
|
UiElement::SubElement { .. } => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scope.rewind(0);
|
//self.scope.rewind(0);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use rhai::ImmutableString;
|
use rhai::ImmutableString;
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc, time::Instant};
|
use std::{collections::HashMap, sync::Arc, time::Instant};
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -10,7 +10,7 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{RenderInput, RenderState};
|
use crate::{RenderInput, RenderState};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum UiElement {
|
pub enum UiElement {
|
||||||
Sprite(UiSprite),
|
Sprite(UiSprite),
|
||||||
RadialBar(UiRadialBar),
|
RadialBar(UiRadialBar),
|
||||||
|
@ -20,7 +20,7 @@ pub enum UiElement {
|
||||||
/// This is a sub-element managed by another element
|
/// This is a sub-element managed by another element
|
||||||
SubElement {
|
SubElement {
|
||||||
parent: ImmutableString,
|
parent: ImmutableString,
|
||||||
element: Rc<RefCell<UiElement>>,
|
element: Box<UiElement>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ impl UiElement {
|
||||||
Self::RadialBar(x) => x.name.clone(),
|
Self::RadialBar(x) => x.name.clone(),
|
||||||
Self::Text(x) => x.name.clone(),
|
Self::Text(x) => x.name.clone(),
|
||||||
Self::Scrollbox(x) => x.name.clone(),
|
Self::Scrollbox(x) => x.name.clone(),
|
||||||
Self::SubElement { element, .. } => element.borrow().get_name(),
|
Self::SubElement { element, .. } => element.get_name(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,11 @@ pub(crate) struct UiConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct UiState {
|
pub(crate) struct UiState {
|
||||||
pub elements: HashMap<ImmutableString, UiElement>,
|
/// The Ui elements on the screen right now
|
||||||
pub names: Vec<ImmutableString>,
|
elements: HashMap<ImmutableString, UiElement>,
|
||||||
|
|
||||||
|
/// Keeps track of element order
|
||||||
|
elements_ordered: Vec<ImmutableString>,
|
||||||
|
|
||||||
pub ct: Arc<Content>,
|
pub ct: Arc<Content>,
|
||||||
current_scene: Option<ImmutableString>,
|
current_scene: Option<ImmutableString>,
|
||||||
|
@ -68,7 +71,7 @@ impl UiState {
|
||||||
Self {
|
Self {
|
||||||
ct,
|
ct,
|
||||||
elements: HashMap::new(),
|
elements: HashMap::new(),
|
||||||
names: Vec::new(),
|
elements_ordered: Vec::new(),
|
||||||
|
|
||||||
current_scene: None,
|
current_scene: None,
|
||||||
show_timings: true,
|
show_timings: true,
|
||||||
|
@ -84,29 +87,32 @@ impl UiState {
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.elements.clear();
|
self.elements.clear();
|
||||||
self.names.clear();
|
self.elements_ordered.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.elements.len()
|
assert!(self.elements.len() == self.elements_ordered.len());
|
||||||
|
return self.elements.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
pub fn contains_name(&self, name: &ImmutableString) -> bool {
|
||||||
pub fn get_by_idx(&self, idx: usize) -> Option<&UiElement> {
|
self.elements.contains_key(name)
|
||||||
self.elements.get(idx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_by_name(&self, name: &ImmutableString) -> Option<&UiElement> {
|
pub fn get_by_name(&self, name: &ImmutableString) -> Option<&UiElement> {
|
||||||
let idx = self.names.get(name);
|
self.elements.get(name)
|
||||||
if idx.is_none() {
|
}
|
||||||
|
|
||||||
|
pub fn get_by_idx(&self, idx: usize) -> Option<&UiElement> {
|
||||||
|
let name = self.elements_ordered.get(idx);
|
||||||
|
if name.is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.get_by_idx(*idx.unwrap())
|
self.elements.get(name.unwrap())
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn get_mut_by_idx(&mut self, idx: usize) -> Option<&mut UiElement> {
|
pub fn get_mut_by_idx(&mut self, idx: usize) -> Option<&mut UiElement> {
|
||||||
let name = self.names.get(idx);
|
let name = self.elements_ordered.get(idx);
|
||||||
if name.is_none() {
|
if name.is_none() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -131,12 +137,19 @@ impl UiState {
|
||||||
self.current_scene = Some(scene);
|
self.current_scene = Some(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, state: &mut RenderState, input: Arc<RenderInput>) {
|
pub fn step(&mut self, state: &mut RenderState, input: &Arc<RenderInput>) {
|
||||||
let t = self.last_step.elapsed().as_secs_f32();
|
let t = self.last_step.elapsed().as_secs_f32();
|
||||||
for (_, e) in &mut self.elements {
|
for (_, e) in &mut self.elements {
|
||||||
match e {
|
match e {
|
||||||
UiElement::Sprite(sprite) => sprite.step(t),
|
UiElement::Sprite(sprite) => sprite.step(t),
|
||||||
UiElement::Scrollbox(sbox) => sbox.step(t),
|
UiElement::Scrollbox(sbox) => sbox.step(t),
|
||||||
|
UiElement::SubElement { element, .. } => match &mut **element {
|
||||||
|
UiElement::Sprite(sprite) => sprite.step(t),
|
||||||
|
UiElement::RadialBar(_) => {}
|
||||||
|
UiElement::Text(..) => {}
|
||||||
|
UiElement::Scrollbox(..) => {}
|
||||||
|
UiElement::SubElement { .. } => {}
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,34 +162,14 @@ impl UiState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_element(&mut self, e: UiElement) {
|
pub fn add_element(&mut self, e: UiElement) {
|
||||||
self.names.push(e.get_name().clone());
|
self.elements_ordered.push(e.get_name().clone());
|
||||||
self.elements.insert(e.get_name().clone(), e);
|
self.elements.insert(e.get_name().clone(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove an element from this sprite.
|
|
||||||
// This does NOT remove subelements from their parent sprites.
|
|
||||||
pub fn remove_element_incomplete(&mut self, name: &ImmutableString) -> Option<UiElement> {
|
|
||||||
let e = self.elements.remove(name);
|
|
||||||
self.names.retain(|x| *x != name);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove an element from this sprite and from all subsprites.
|
// Remove an element from this sprite and from all subsprites.
|
||||||
pub fn remove_element(&mut self, name: &ImmutableString) {
|
pub fn remove_element(&mut self, name: &ImmutableString) {
|
||||||
let e = self.elements.remove(name);
|
let _ = self.elements.remove(name);
|
||||||
self.names.retain(|x| *x != name);
|
self.elements_ordered.retain(|x| *x != name);
|
||||||
|
|
||||||
match e {
|
|
||||||
Some(UiElement::SubElement { parent, element }) => {
|
|
||||||
let x = Rc::into_inner(element).unwrap().into_inner();
|
|
||||||
let parent = self.elements.get_mut(&parent).unwrap();
|
|
||||||
match parent {
|
|
||||||
UiElement::Scrollbox(s) => s.remove_element(&x.get_name()),
|
|
||||||
_ => unreachable!("invalid subelement parent"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +189,18 @@ impl<'a> UiState {
|
||||||
for e in self.elements.values() {
|
for e in self.elements.values() {
|
||||||
match &e {
|
match &e {
|
||||||
UiElement::Text(t) => v.push(t.get_textarea(input, window)),
|
UiElement::Text(t) => v.push(t.get_textarea(input, window)),
|
||||||
UiElement::Scrollbox(b) => v.extend(b.get_textareas(input, window)),
|
UiElement::SubElement { parent, element } => {
|
||||||
|
let parent = self.get_by_name(parent).unwrap();
|
||||||
|
match parent {
|
||||||
|
UiElement::Scrollbox(sbox) => match &**element {
|
||||||
|
UiElement::Text(x) => {
|
||||||
|
v.push(x.get_textarea_with_offset(input, window, sbox.offset))
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue