mirror of
https://github.com/gopher64/gopher64.git
synced 2026-07-11 01:25:20 +02:00
hold a ref to AndroidApp (#928)
* hold a ref to AndroidApp * more * more * more * more * more
This commit is contained in:
Generated
-1
@@ -2249,7 +2249,6 @@ dependencies = [
|
||||
"futures",
|
||||
"jni 0.22.4",
|
||||
"mimalloc",
|
||||
"ndk-context",
|
||||
"open",
|
||||
"postcard",
|
||||
"rand 0.10.1",
|
||||
|
||||
@@ -47,7 +47,6 @@ open = { version = "5.3", optional = true }
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
slint = { version = "1.16", default-features = false, features = ["compat-1-2", "std", "backend-android-activity-06", "accessibility"], optional = true }
|
||||
jni = "0.22"
|
||||
ndk-context = "0.1"
|
||||
|
||||
[build-dependencies]
|
||||
winresource = "0.1"
|
||||
|
||||
+2
-7
@@ -311,13 +311,7 @@ pub async fn run() -> std::io::Result<()> {
|
||||
async fn android_main(app: slint::android::AndroidApp) {
|
||||
slint::android::init(app.clone()).unwrap();
|
||||
|
||||
if let Err(_) = android::DIRS.set(ui::Dirs {
|
||||
config_dir: app.internal_data_path().unwrap().join("config"),
|
||||
data_dir: app.external_data_path().unwrap().join("data"),
|
||||
cache_dir: app.internal_data_path().unwrap().join("cache"),
|
||||
}) {
|
||||
println!("Android dirs already set");
|
||||
}
|
||||
*android::ANDROID_APP.lock().unwrap() = Some(app);
|
||||
|
||||
let dirs = ui::get_dirs();
|
||||
|
||||
@@ -327,4 +321,5 @@ async fn android_main(app: slint::android::AndroidApp) {
|
||||
std::fs::create_dir_all(dirs.data_dir.join("states")).unwrap();
|
||||
|
||||
ui::gui::app_window(true);
|
||||
*android::ANDROID_APP.lock().unwrap() = None;
|
||||
}
|
||||
|
||||
@@ -128,32 +128,29 @@ pub fn sdl_close() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub fn get_dirs() -> Dirs {
|
||||
let exe_path = std::env::current_exe().unwrap();
|
||||
let portable_dir = exe_path.parent();
|
||||
let portable = portable_dir.unwrap().join("portable.txt").exists();
|
||||
if portable {
|
||||
Dirs {
|
||||
config_dir: portable_dir.unwrap().join("portable_data").join("config"),
|
||||
data_dir: portable_dir.unwrap().join("portable_data").join("data"),
|
||||
cache_dir: portable_dir.unwrap().join("portable_data").join("cache"),
|
||||
}
|
||||
} else {
|
||||
Dirs {
|
||||
config_dir: dirs::config_dir().unwrap().join("gopher64"),
|
||||
data_dir: dirs::data_dir().unwrap().join("gopher64"),
|
||||
cache_dir: dirs::cache_dir().unwrap().join("gopher64"),
|
||||
#[cfg(not(target_os = "android"))]
|
||||
{
|
||||
let exe_path = std::env::current_exe().unwrap();
|
||||
let portable_dir = exe_path.parent();
|
||||
let portable = portable_dir.unwrap().join("portable.txt").exists();
|
||||
if portable {
|
||||
Dirs {
|
||||
config_dir: portable_dir.unwrap().join("portable_data").join("config"),
|
||||
data_dir: portable_dir.unwrap().join("portable_data").join("data"),
|
||||
cache_dir: portable_dir.unwrap().join("portable_data").join("cache"),
|
||||
}
|
||||
} else {
|
||||
Dirs {
|
||||
config_dir: dirs::config_dir().unwrap().join("gopher64"),
|
||||
data_dir: dirs::data_dir().unwrap().join("gopher64"),
|
||||
cache_dir: dirs::cache_dir().unwrap().join("gopher64"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn get_dirs() -> Dirs {
|
||||
if let Some(android_dirs) = android::DIRS.get() {
|
||||
android_dirs.clone()
|
||||
} else {
|
||||
panic!("Android app not initialized");
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
android::get_dirs()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
-25
@@ -3,10 +3,12 @@ use jni::refs::Global;
|
||||
use jni::{Env, JavaVM, bind_java_type};
|
||||
|
||||
use crate::ui;
|
||||
pub static DIRS: std::sync::OnceLock<ui::Dirs> = std::sync::OnceLock::new();
|
||||
|
||||
pub static ANDROID_APP: std::sync::Mutex<Option<slint::android::AndroidApp>> =
|
||||
std::sync::Mutex::new(None);
|
||||
|
||||
bind_java_type! {
|
||||
AndroidContext => "android.content.Context",
|
||||
AndroidActivity => "android.app.Activity",
|
||||
type_map = {
|
||||
AndroidIntent => "android.content.Intent",
|
||||
},
|
||||
@@ -72,9 +74,14 @@ pub struct ControllerInfo {
|
||||
|
||||
/// Lists connected gamepads and joysticks using the Android framework.
|
||||
pub fn list_controllers() -> Vec<ControllerInfo> {
|
||||
let ctx = ndk_context::android_context();
|
||||
|
||||
let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) };
|
||||
let vm = if let Ok(app) = ANDROID_APP.lock()
|
||||
&& let Some(app) = app.as_ref()
|
||||
{
|
||||
unsafe { JavaVM::from_raw(app.vm_as_ptr().cast()) }
|
||||
} else {
|
||||
eprintln!("Android app not initialized");
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
match vm.attach_current_thread(list_controllers_on_jvm) {
|
||||
Ok(controllers) => controllers,
|
||||
@@ -150,35 +157,39 @@ fn list_controllers_on_jvm(env: &mut Env<'_>) -> jni::errors::Result<Vec<Control
|
||||
|
||||
/// Opens a URI in the user's default app via [`Intent::ACTION_VIEW`](https://developer.android.com/reference/android/content/Intent#ACTION_VIEW).
|
||||
pub fn open_uri(path: &str) {
|
||||
let ctx = ndk_context::android_context();
|
||||
|
||||
let path = path.to_string();
|
||||
|
||||
let vm = unsafe { JavaVM::from_raw(ctx.vm().cast()) };
|
||||
if let Err(err) = vm.attach_current_thread(|env| open_uri_on_jvm(env, ctx.context(), &path)) {
|
||||
let vm = if let Ok(app) = ANDROID_APP.lock()
|
||||
&& let Some(app) = app.as_ref()
|
||||
{
|
||||
unsafe { JavaVM::from_raw(app.vm_as_ptr().cast()) }
|
||||
} else {
|
||||
eprintln!("Android app not initialized");
|
||||
return;
|
||||
};
|
||||
if let Err(err) = vm.attach_current_thread(|env| open_uri_on_jvm(env, &path)) {
|
||||
eprintln!("JNI error while opening URI: {err:?}");
|
||||
}
|
||||
}
|
||||
|
||||
fn open_uri_on_jvm(
|
||||
env: &mut Env<'_>,
|
||||
context: *mut std::ffi::c_void,
|
||||
path: &str,
|
||||
) -> jni::errors::Result<()> {
|
||||
let context_ptr = context.cast();
|
||||
let context = unsafe { env.as_cast_raw::<Global<AndroidContext>>(&context_ptr)? };
|
||||
fn open_uri_on_jvm(env: &mut Env<'_>, path: &str) -> jni::errors::Result<()> {
|
||||
if let Ok(app) = ANDROID_APP.lock()
|
||||
&& let Some(app) = app.as_ref()
|
||||
{
|
||||
let raw_activity_global = app.activity_as_ptr() as jni::sys::jobject;
|
||||
let activity = unsafe { env.as_cast_raw::<Global<AndroidActivity>>(&raw_activity_global)? };
|
||||
|
||||
let uri_string = JString::from_str(env, path.to_string())?;
|
||||
let uri = AndroidUri::parse(env, &uri_string)?;
|
||||
let uri_string = JString::from_str(env, path.to_string())?;
|
||||
let uri = AndroidUri::parse(env, &uri_string)?;
|
||||
|
||||
let action_view = AndroidIntent::ACTION_VIEW(env)?;
|
||||
let flag = AndroidIntent::FLAG_ACTIVITY_NEW_TASK(env)?;
|
||||
let intent = AndroidIntent::new(env, &action_view)?
|
||||
.set_data(env, &uri)?
|
||||
.add_flags(env, flag)?;
|
||||
let action_view = AndroidIntent::ACTION_VIEW(env)?;
|
||||
let intent = AndroidIntent::new(env, &action_view)?.set_data(env, &uri)?;
|
||||
|
||||
context.as_ref().start_activity(env, &intent)?;
|
||||
Ok(())
|
||||
activity.as_ref().start_activity(env, &intent)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(jni::errors::Error::UninitializedJavaVM)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn select_rom(_rom_dir: slint::SharedString) -> Option<std::path::PathBuf> {
|
||||
@@ -192,3 +203,17 @@ pub async fn select_gb_rom(_player: i32) -> Option<std::path::PathBuf> {
|
||||
pub async fn select_gb_ram(_player: i32) -> Option<std::path::PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_dirs() -> ui::Dirs {
|
||||
if let Ok(app) = ANDROID_APP.lock()
|
||||
&& let Some(app) = app.as_ref()
|
||||
{
|
||||
ui::Dirs {
|
||||
config_dir: app.internal_data_path().unwrap().join("config"),
|
||||
data_dir: app.external_data_path().unwrap().join("data"),
|
||||
cache_dir: app.internal_data_path().unwrap().join("cache"),
|
||||
}
|
||||
} else {
|
||||
panic!("Android app not initialized");
|
||||
}
|
||||
}
|
||||
|
||||
+28
-28
@@ -105,34 +105,34 @@ export component AppWindow inherits Window {
|
||||
portrait: root.is-portrait;
|
||||
title: "Gopher64";
|
||||
model: [
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/local_game.svg"),
|
||||
label: @tr("Menu" => "Local Game"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/controller.svg"),
|
||||
label: @tr("Menu" => "Controller Config"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/netplay.svg"),
|
||||
label: @tr("Menu" => "Netplay"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/cheats.svg"),
|
||||
label: @tr("Menu" => "Cheats"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/achievements.svg"),
|
||||
label: @tr("Menu" => "RetroAchievements"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/settings.svg"),
|
||||
label: @tr("Menu" => "Settings"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/about.svg"),
|
||||
label: @tr("Menu" => "About"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/local_game.svg"),
|
||||
label: @tr("Menu" => "Local Game"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/controller.svg"),
|
||||
label: @tr("Menu" => "Controller Config"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/netplay.svg"),
|
||||
label: @tr("Menu" => "Netplay"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/cheats.svg"),
|
||||
label: @tr("Menu" => "Cheats"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/achievements.svg"),
|
||||
label: @tr("Menu" => "RetroAchievements"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/settings.svg"),
|
||||
label: @tr("Menu" => "Settings"),
|
||||
},
|
||||
{
|
||||
icon: @image-url("../../../data/ui/icons/about.svg"),
|
||||
label: @tr("Menu" => "About"),
|
||||
},
|
||||
];
|
||||
x: root.safe-left;
|
||||
y: root.is-portrait ? parent.height - root.bar-thickness - root.safe-bottom : root.safe-top;
|
||||
|
||||
@@ -13,6 +13,10 @@ export component InputProfileDialog inherits Window {
|
||||
in-out property profile_name <=> InputProfileData.profile_name;
|
||||
in-out property deadzone <=> InputProfileData.deadzone;
|
||||
VerticalBox {
|
||||
padding-left: root.safe-area-insets.left;
|
||||
padding-top: root.safe-area-insets.top;
|
||||
padding-right: root.safe-area-insets.right;
|
||||
padding-bottom: root.safe-area-insets.bottom;
|
||||
alignment: start;
|
||||
|
||||
HorizontalBox {
|
||||
|
||||
+112
-91
@@ -1,8 +1,10 @@
|
||||
import { LineEdit, HorizontalBox, VerticalBox, Button, ComboBox } from "std-widgets.slint";
|
||||
import { LineEdit, HorizontalBox, VerticalBox, Button, ComboBox, ScrollView } from "std-widgets.slint";
|
||||
import { NetplayData } from "netplay_page.slint";
|
||||
|
||||
export component NetplayCreate inherits Window {
|
||||
title: @tr("Netplay: Create Session");
|
||||
preferred-width: 425px;
|
||||
preferred-height: 425px;
|
||||
in-out property server_names <=> NetplayData.server_names;
|
||||
in-out property server_urls <=> NetplayData.server_urls;
|
||||
in-out property custom_server_url <=> NetplayData.custom_server_url;
|
||||
@@ -18,108 +20,121 @@ export component NetplayCreate inherits Window {
|
||||
callback create_session(string, string, string, string, string, string, string);
|
||||
|
||||
VerticalBox {
|
||||
HorizontalBox {
|
||||
vertical-stretch: 1;
|
||||
padding-left: root.safe-area-insets.left;
|
||||
padding-top: root.safe-area-insets.top;
|
||||
padding-right: root.safe-area-insets.right;
|
||||
padding-bottom: root.safe-area-insets.bottom;
|
||||
ScrollView {
|
||||
mouse-drag-pan-enabled: true;
|
||||
vertical-stretch: 1;
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Session Name:");
|
||||
}
|
||||
alignment: start;
|
||||
HorizontalBox {
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Session Name:");
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter session name");
|
||||
edited(text) => {
|
||||
NetplayData.session_name = text;
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter session name");
|
||||
edited(text) => {
|
||||
NetplayData.session_name = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Password (Optional):");
|
||||
}
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Password (Optional):");
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter session password");
|
||||
edited(text) => {
|
||||
NetplayData.password = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Player Name:");
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter player name");
|
||||
edited(text) => {
|
||||
NetplayData.player_name = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Server:");
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
server := ComboBox {
|
||||
model: NetplayData.server_names;
|
||||
selected => {
|
||||
if (self.current-index >= 0) {
|
||||
if (self.current-value == "Custom") {
|
||||
get_custom_url();
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter session password");
|
||||
edited(text) => {
|
||||
NetplayData.password = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
alignment: center;
|
||||
if NetplayData.game_name == "": Button {
|
||||
text: @tr("Select ROM");
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Player Name:");
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter player name");
|
||||
edited(text) => {
|
||||
NetplayData.player_name = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if NetplayData.game_name != "": Button {
|
||||
text: NetplayData.game_name;
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Server:");
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
server := ComboBox {
|
||||
model: NetplayData.server_names;
|
||||
selected => {
|
||||
if (self.current-index >= 0) {
|
||||
if (self.current-value == "Custom") {
|
||||
get_custom_url();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { }
|
||||
|
||||
if NetplayData.game_cheats != "":Text {
|
||||
text: @tr("This game has cheats enabled. Cheats will be synchronized with other players.");
|
||||
}
|
||||
Rectangle { }
|
||||
|
||||
Button {
|
||||
property <string> server_url;
|
||||
text: @tr("Create Session");
|
||||
enabled: NetplayData.session_name != "" && NetplayData.player_name != "" && NetplayData.game_name != "" && !NetplayData.pending_session;
|
||||
clicked => {
|
||||
NetplayData.pending_session = true;
|
||||
if (server.current-value == "Custom") {
|
||||
server_url = "ws://" + NetplayData.custom_server_url;
|
||||
} else {
|
||||
server_url = NetplayData.server_urls[server.current-index];
|
||||
HorizontalBox {
|
||||
alignment: center;
|
||||
if NetplayData.game_name == "": Button {
|
||||
text: @tr("Select ROM");
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
}
|
||||
}
|
||||
if NetplayData.game_name != "": Button {
|
||||
text: NetplayData.game_name;
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
create_session(
|
||||
server_url,
|
||||
NetplayData.session_name,
|
||||
NetplayData.player_name,
|
||||
NetplayData.game_name,
|
||||
NetplayData.game_hash,
|
||||
NetplayData.game_cheats,
|
||||
NetplayData.password);
|
||||
|
||||
Rectangle { }
|
||||
|
||||
if NetplayData.game_cheats != "":Text {
|
||||
text: @tr("This game has cheats enabled. Cheats will be synchronized with other players.");
|
||||
}
|
||||
Rectangle { }
|
||||
|
||||
Button {
|
||||
property <string> server_url;
|
||||
text: @tr("Create Session");
|
||||
enabled: NetplayData.session_name != "" && NetplayData.player_name != "" && NetplayData.game_name != "" && !NetplayData.pending_session;
|
||||
clicked => {
|
||||
NetplayData.pending_session = true;
|
||||
if (server.current-value == "Custom") {
|
||||
server_url = "ws://" + NetplayData.custom_server_url;
|
||||
} else {
|
||||
server_url = NetplayData.server_urls[server.current-index];
|
||||
}
|
||||
create_session(
|
||||
server_url,
|
||||
NetplayData.session_name,
|
||||
NetplayData.player_name,
|
||||
NetplayData.game_name,
|
||||
NetplayData.game_hash,
|
||||
NetplayData.game_cheats,
|
||||
NetplayData.password);
|
||||
}
|
||||
}
|
||||
Rectangle { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +142,13 @@ export component NetplayCreate inherits Window {
|
||||
|
||||
export component DispatcherDialog inherits Dialog {
|
||||
title: @tr("Creating Server");
|
||||
Text {
|
||||
text: @tr("Creating server, please wait...This may take about 30 seconds.");
|
||||
VerticalBox {
|
||||
padding-left: root.safe-area-insets.left;
|
||||
padding-top: root.safe-area-insets.top;
|
||||
padding-right: root.safe-area-insets.right;
|
||||
padding-bottom: root.safe-area-insets.bottom;
|
||||
Text {
|
||||
text: @tr("Creating server, please wait...This may take about 30 seconds.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+108
-95
@@ -1,4 +1,4 @@
|
||||
import { VerticalBox, LineEdit, StandardTableView, HorizontalBox, ComboBox, Button } from "std-widgets.slint";
|
||||
import { VerticalBox, LineEdit, StandardTableView, HorizontalBox, ComboBox, Button, ScrollView } from "std-widgets.slint";
|
||||
import { NetplayData } from "netplay_page.slint";
|
||||
|
||||
export component NetplayJoin inherits Window {
|
||||
@@ -22,116 +22,129 @@ export component NetplayJoin inherits Window {
|
||||
callback select_rom(string);
|
||||
callback join_session(string, string, string, string, int);
|
||||
preferred-width: 700px;
|
||||
preferred-height: 450px;
|
||||
|
||||
VerticalBox {
|
||||
HorizontalBox {
|
||||
vertical-stretch: 1;
|
||||
padding-left: root.safe-area-insets.left;
|
||||
padding-top: root.safe-area-insets.top;
|
||||
padding-right: root.safe-area-insets.right;
|
||||
padding-bottom: root.safe-area-insets.bottom;
|
||||
ScrollView {
|
||||
mouse-drag-pan-enabled: true;
|
||||
vertical-stretch: 1;
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Player Name (required):");
|
||||
}
|
||||
alignment: start;
|
||||
HorizontalBox {
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Player Name (required):");
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter player name");
|
||||
edited(text) => {
|
||||
NetplayData.player_name = text;
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Enter player name");
|
||||
edited(text) => {
|
||||
NetplayData.player_name = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Custom Server URL (optional):");
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder-text: "example.com:45000";
|
||||
text: NetplayData.custom_server_url;
|
||||
edited(text) => {
|
||||
NetplayData.custom_server_url = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Custom Server URL (optional):");
|
||||
if NetplayData.pending_refresh: Text {
|
||||
text: @tr("Refreshing session list, please wait...");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
placeholder-text: "example.com:45000";
|
||||
text: NetplayData.custom_server_url;
|
||||
edited(text) => {
|
||||
NetplayData.custom_server_url = text;
|
||||
if !NetplayData.pending_refresh && NetplayData.sessions.length > 0: Text {
|
||||
text: @tr("Click on a session below to select it");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
if !NetplayData.pending_refresh && NetplayData.sessions.length == 0: Text {
|
||||
text: @tr("No sessions available. Please refresh to check again.");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
StandardTableView {
|
||||
columns: [
|
||||
{ title: @tr("Server") },
|
||||
{ title: @tr("Session Name") },
|
||||
{ title: @tr("Game Name") },
|
||||
{ title: @tr("Password") },
|
||||
{ title: @tr("Cheats") }
|
||||
];
|
||||
rows: NetplayData.sessions;
|
||||
current-row: NetplayData.current_session;
|
||||
current-row-changed(current-row) => {
|
||||
NetplayData.current_session = current-row;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if NetplayData.pending_refresh: Text {
|
||||
text: @tr("Refreshing session list, please wait...");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
if !NetplayData.pending_refresh && NetplayData.sessions.length > 0: Text {
|
||||
text: @tr("Click on a session below to select it");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
if !NetplayData.pending_refresh && NetplayData.sessions.length == 0: Text {
|
||||
text: @tr("No sessions available. Please refresh to check again.");
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
StandardTableView {
|
||||
columns: [
|
||||
{ title: @tr("Server") },
|
||||
{ title: @tr("Session Name") },
|
||||
{ title: @tr("Game Name") },
|
||||
{ title: @tr("Password") },
|
||||
{ title: @tr("Cheats") }
|
||||
];
|
||||
rows: NetplayData.sessions;
|
||||
current-row: NetplayData.current_session;
|
||||
current-row-changed(current-row) => {
|
||||
NetplayData.current_session = current-row;
|
||||
}
|
||||
}
|
||||
HorizontalBox {
|
||||
Rectangle { }
|
||||
|
||||
HorizontalBox {
|
||||
Rectangle { }
|
||||
|
||||
Button {
|
||||
enabled: !NetplayData.pending_refresh;
|
||||
text: @tr("Refresh Session List");
|
||||
clicked => {
|
||||
NetplayData.pending_refresh = true;
|
||||
NetplayData.current_session = -1;
|
||||
NetplayData.sessions = [];
|
||||
NetplayData.room_urls = [];
|
||||
NetplayData.room_ports = [];
|
||||
refresh_session();
|
||||
Button {
|
||||
enabled: !NetplayData.pending_refresh;
|
||||
text: @tr("Refresh Session List");
|
||||
clicked => {
|
||||
NetplayData.pending_refresh = true;
|
||||
NetplayData.current_session = -1;
|
||||
NetplayData.sessions = [];
|
||||
NetplayData.room_urls = [];
|
||||
NetplayData.room_ports = [];
|
||||
refresh_session();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Password (if required)");
|
||||
edited(text) => {
|
||||
NetplayData.password = text;
|
||||
}
|
||||
}
|
||||
HorizontalBox {
|
||||
LineEdit {
|
||||
placeholder-text: @tr("Password (if required)");
|
||||
edited(text) => {
|
||||
NetplayData.password = text;
|
||||
}
|
||||
}
|
||||
|
||||
if NetplayData.game_name == "": Button {
|
||||
text: @tr("Select ROM");
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
}
|
||||
}
|
||||
if NetplayData.game_name != "": Button {
|
||||
text: NetplayData.game_name;
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
}
|
||||
}
|
||||
if NetplayData.game_name == "": Button {
|
||||
text: @tr("Select ROM");
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
}
|
||||
}
|
||||
if NetplayData.game_name != "": Button {
|
||||
text: NetplayData.game_name;
|
||||
clicked => {
|
||||
select_rom(NetplayData.rom_dir);
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: @tr("Join Session");
|
||||
enabled: NetplayData.player_name != "" && NetplayData.current_session >= 0 && NetplayData.game_name != "" && !NetplayData.pending_session;
|
||||
clicked => {
|
||||
NetplayData.pending_session = true;
|
||||
join_session(
|
||||
NetplayData.player_name,
|
||||
NetplayData.game_hash,
|
||||
NetplayData.password,
|
||||
NetplayData.room_urls[NetplayData.current_session],
|
||||
NetplayData.room_ports[NetplayData.current_session]);
|
||||
Button {
|
||||
text: @tr("Join Session");
|
||||
enabled: NetplayData.player_name != "" && NetplayData.current_session >= 0 && NetplayData.game_name != "" && !NetplayData.pending_session;
|
||||
clicked => {
|
||||
NetplayData.pending_session = true;
|
||||
join_session(
|
||||
NetplayData.player_name,
|
||||
NetplayData.game_hash,
|
||||
NetplayData.password,
|
||||
NetplayData.room_urls[NetplayData.current_session],
|
||||
NetplayData.room_ports[NetplayData.current_session]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,12 +82,22 @@ export component CustomNetplayServer inherits Dialog {
|
||||
title: @tr("Custom Netplay Server");
|
||||
preferred-width: 400px;
|
||||
callback ok_clicked(string);
|
||||
server_url := LineEdit {
|
||||
text: NetplayData.custom_server_url;
|
||||
placeholder-text: "example.com:45000";
|
||||
VerticalBox {
|
||||
alignment: start;
|
||||
vertical-stretch: 1;
|
||||
padding-left: root.safe-area-insets.left;
|
||||
padding-top: root.safe-area-insets.top;
|
||||
padding-right: root.safe-area-insets.right;
|
||||
padding-bottom: root.safe-area-insets.bottom;
|
||||
server_url := LineEdit {
|
||||
vertical-stretch: 0;
|
||||
text: NetplayData.custom_server_url;
|
||||
placeholder-text: "example.com:45000";
|
||||
}
|
||||
}
|
||||
|
||||
StandardButton {
|
||||
vertical-stretch: 0;
|
||||
kind: ok;
|
||||
clicked => {
|
||||
NetplayData.custom_server_url = server_url.text;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VerticalBox, HorizontalBox, TextEdit, LineEdit, Button, GridBox } from "std-widgets.slint";
|
||||
import { VerticalBox, HorizontalBox, TextEdit, LineEdit, Button, GridBox, ScrollView } from "std-widgets.slint";
|
||||
import { NetplayData } from "netplay_page.slint";
|
||||
|
||||
export global NetplayWaitData {
|
||||
@@ -12,8 +12,8 @@ export global NetplayWaitData {
|
||||
|
||||
export component NetplayWait inherits Window {
|
||||
title: @tr("Netplay: Waiting Room");
|
||||
preferred-width: 400px;
|
||||
preferred-height: 400px;
|
||||
preferred-width: 500px;
|
||||
preferred-height: 450px;
|
||||
in-out property players <=> NetplayWaitData.players;
|
||||
in-out property session_name <=> NetplayData.session_name;
|
||||
in-out property game_name <=> NetplayData.game_name;
|
||||
@@ -27,83 +27,99 @@ export component NetplayWait inherits Window {
|
||||
callback begin_game;
|
||||
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: @tr("Session Name:") + " " + NetplayData.session_name;
|
||||
font-size: 15px;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: @tr("Game Name:") + " " + NetplayData.game_name;
|
||||
font-size: 15px;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: @tr("Ping:") + " " + NetplayWaitData.ping;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
|
||||
GridBox {
|
||||
Row {
|
||||
vertical-stretch: 1;
|
||||
padding-left: root.safe-area-insets.left;
|
||||
padding-top: root.safe-area-insets.top;
|
||||
padding-right: root.safe-area-insets.right;
|
||||
padding-bottom: root.safe-area-insets.bottom;
|
||||
ScrollView {
|
||||
mouse-drag-pan-enabled: true;
|
||||
vertical-stretch: 1;
|
||||
VerticalBox {
|
||||
alignment: start;
|
||||
Text {
|
||||
text: @tr("Player 1:") + " " + players[0];
|
||||
text: @tr("Session Name:") + " " + NetplayData.session_name;
|
||||
font-size: 15px;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: @tr("Player 2:") + " " + players[1];
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Text {
|
||||
text: @tr("Player 3:") + " " + players[2];
|
||||
text: @tr("Game Name:") + " " + NetplayData.game_name;
|
||||
font-size: 15px;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: @tr("Player 4:") + " " + players[3];
|
||||
text: @tr("Ping:") + " " + NetplayWaitData.ping;
|
||||
horizontal-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextEdit {
|
||||
read-only: true;
|
||||
text: NetplayWaitData.chat_text;
|
||||
}
|
||||
GridBox {
|
||||
Row {
|
||||
Text {
|
||||
text: @tr("Player 1:") + " " + players[0];
|
||||
}
|
||||
|
||||
HorizontalBox {
|
||||
chat_message := LineEdit {
|
||||
placeholder-text: @tr("Enter chat message");
|
||||
key-released(event) => {
|
||||
if (event.text == Key.Return && chat_message.text != "") {
|
||||
send_chat_message(chat_message.text);
|
||||
chat_message.text = "";
|
||||
Text {
|
||||
text: @tr("Player 2:") + " " + players[1];
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Text {
|
||||
text: @tr("Player 3:") + " " + players[2];
|
||||
}
|
||||
|
||||
Text {
|
||||
text: @tr("Player 4:") + " " + players[3];
|
||||
}
|
||||
}
|
||||
accept
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: @tr("Send Message");
|
||||
enabled: chat_message.text != "";
|
||||
clicked => {
|
||||
send_chat_message(chat_message.text);
|
||||
chat_message.text = "";
|
||||
TextEdit {
|
||||
read-only: true;
|
||||
min-height: 120px;
|
||||
text: NetplayWaitData.chat_text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: NetplayWaitData.motd;
|
||||
wrap: word-wrap;
|
||||
}
|
||||
HorizontalBox {
|
||||
chat_message := LineEdit {
|
||||
placeholder-text: @tr("Enter chat message");
|
||||
key-released(event) => {
|
||||
if (event.text == Key.Return && chat_message.text != "") {
|
||||
send_chat_message(chat_message.text);
|
||||
chat_message.text = "";
|
||||
}
|
||||
accept
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: @tr("Start Game");
|
||||
enabled: NetplayWaitData.can_start;
|
||||
clicked => {
|
||||
NetplayWaitData.can_start = false;
|
||||
begin_game();
|
||||
Button {
|
||||
text: @tr("Send Message");
|
||||
enabled: chat_message.text != "";
|
||||
clicked => {
|
||||
send_chat_message(chat_message.text);
|
||||
chat_message.text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: NetplayWaitData.motd;
|
||||
wrap: word-wrap;
|
||||
}
|
||||
|
||||
Button {
|
||||
text: @tr("Start Game");
|
||||
enabled: NetplayWaitData.can_start;
|
||||
clicked => {
|
||||
NetplayWaitData.can_start = false;
|
||||
begin_game();
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
vertical-stretch: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export component RetroAchievements inherits Page {
|
||||
edited(text) => {
|
||||
RAData.ra_username = text;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
password_widget := LineEdit {
|
||||
enabled: RAData.enabled;
|
||||
|
||||
+14
-14
@@ -46,20 +46,20 @@ component SideBarButton inherits Rectangle {
|
||||
}
|
||||
|
||||
states [
|
||||
selected when root.selected: {
|
||||
highlight.opacity: 1;
|
||||
highlight.background: Palette.accent-background;
|
||||
icon-display.colorize: Palette.accent-foreground;
|
||||
}
|
||||
pressed when touch.pressed && !root.selected: {
|
||||
highlight.opacity: 0.55;
|
||||
}
|
||||
hover when touch.has-hover && !root.selected: {
|
||||
highlight.opacity: 0.4;
|
||||
}
|
||||
focused when root.has-focus && !root.selected: {
|
||||
highlight.opacity: 0.4;
|
||||
}
|
||||
selected when root.selected: {
|
||||
highlight.opacity: 1;
|
||||
highlight.background: Palette.accent-background;
|
||||
icon-display.colorize: Palette.accent-foreground;
|
||||
}
|
||||
pressed when touch.pressed && !root.selected: {
|
||||
highlight.opacity: 0.55;
|
||||
}
|
||||
hover when touch.has-hover && !root.selected: {
|
||||
highlight.opacity: 0.4;
|
||||
}
|
||||
focused when root.has-focus && !root.selected: {
|
||||
highlight.opacity: 0.4;
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user