mirror of
https://github.com/yuzu-emu/liftinstall.git
synced 2024-11-25 22:05:38 +01:00
feat(ui): migrate UI/Web framework to WRY
This commit is contained in:
parent
0d4022d348
commit
6e7d045794
1153
Cargo.lock
generated
1153
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,8 @@ description = "An adaptable installer for your application."
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
web-view = { version = "0.7", features = ["edge"] }
|
anyhow = "^1"
|
||||||
|
wry = "0.12"
|
||||||
tinyfiledialogs = "3.8"
|
tinyfiledialogs = "3.8"
|
||||||
|
|
||||||
hyper = "0.11.27"
|
hyper = "0.11.27"
|
||||||
|
@ -16,7 +16,7 @@ pub fn launch(app_name: &str, is_launcher: bool, framework: InstallerFramework)
|
|||||||
|
|
||||||
let (servers, address) = rest::server::spawn_servers(framework.clone());
|
let (servers, address) = rest::server::spawn_servers(framework.clone());
|
||||||
|
|
||||||
ui::start_ui(app_name, &address, is_launcher);
|
ui::start_ui(app_name, &address, is_launcher).log_expect("Failed to start UI");
|
||||||
|
|
||||||
// Explicitly hint that we want the servers instance until here.
|
// Explicitly hint that we want the servers instance until here.
|
||||||
drop(servers);
|
drop(servers);
|
||||||
|
@ -2,9 +2,16 @@
|
|||||||
//!
|
//!
|
||||||
//! Provides a web-view UI.
|
//! Provides a web-view UI.
|
||||||
|
|
||||||
use web_view::Content;
|
use anyhow::Result;
|
||||||
|
use wry::{
|
||||||
use crate::logging::LoggingErrors;
|
application::{
|
||||||
|
dpi::LogicalSize,
|
||||||
|
event::{Event, StartCause, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
window::WindowBuilder,
|
||||||
|
},
|
||||||
|
webview::{RpcResponse, WebViewBuilder},
|
||||||
|
};
|
||||||
|
|
||||||
use log::Level;
|
use log::Level;
|
||||||
|
|
||||||
@ -16,55 +23,56 @@ enum CallbackType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the main web UI. Will return when UI is closed.
|
/// Starts the main web UI. Will return when UI is closed.
|
||||||
pub fn start_ui(app_name: &str, http_address: &str, is_launcher: bool) {
|
pub fn start_ui(app_name: &str, http_address: &str, is_launcher: bool) -> Result<()> {
|
||||||
let size = if is_launcher { (600, 300) } else { (1024, 600) };
|
let size = if is_launcher { (600, 300) } else { (1024, 600) };
|
||||||
|
|
||||||
info!("Spawning web view instance");
|
info!("Spawning web view instance");
|
||||||
|
|
||||||
web_view::builder()
|
let event_loop = EventLoop::new();
|
||||||
.title(&format!("{} Installer", app_name))
|
let window = WindowBuilder::new()
|
||||||
.content(Content::Url(http_address))
|
.with_title(format!("{} Installer", app_name))
|
||||||
.size(size.0, size.1)
|
.with_inner_size(LogicalSize::new(size.0, size.1))
|
||||||
.resizable(false)
|
.with_resizable(false)
|
||||||
.debug(cfg!(debug_assertions))
|
.build(&event_loop)?;
|
||||||
.user_data(())
|
let _webview = WebViewBuilder::new(window)?
|
||||||
.invoke_handler(|wv, msg| {
|
.with_url(http_address)?
|
||||||
let mut cb_result = Ok(());
|
.with_rpc_handler(|_, mut event| {
|
||||||
let command: CallbackType =
|
debug!("Incoming payload: {:?}", event);
|
||||||
serde_json::from_str(msg).log_expect(&format!("Unable to parse string: {:?}", msg));
|
match event.method.as_str() {
|
||||||
|
"Test" => (),
|
||||||
debug!("Incoming payload: {:?}", command);
|
"Log" => {
|
||||||
|
if let Some(msg) = event.params.take() {
|
||||||
match command {
|
if let Ok(msg) = serde_json::from_value::<(String, String)>(msg) {
|
||||||
CallbackType::SelectInstallDir { callback_name } => {
|
let kind = match msg.0.as_str() {
|
||||||
let result =
|
|
||||||
tinyfiledialogs::select_folder_dialog("Select a install directory...", "");
|
|
||||||
|
|
||||||
if let Some(new_path) = result {
|
|
||||||
if !new_path.is_empty() {
|
|
||||||
let result = serde_json::to_string(&new_path)
|
|
||||||
.log_expect("Unable to serialize response");
|
|
||||||
let command = format!("window.{}({});", callback_name, result);
|
|
||||||
debug!("Injecting response: {}", command);
|
|
||||||
cb_result = wv.eval(&command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CallbackType::Log { msg, kind } => {
|
|
||||||
let kind = match kind.as_ref() {
|
|
||||||
"info" | "log" => Level::Info,
|
"info" | "log" => Level::Info,
|
||||||
"warn" => Level::Warn,
|
"warn" => Level::Warn,
|
||||||
"error" => Level::Error,
|
|
||||||
_ => Level::Error,
|
_ => Level::Error,
|
||||||
};
|
};
|
||||||
|
log!(target: "liftinstall::frontend::js", kind, "{}", msg.1);
|
||||||
log!(target: "liftinstall::frontend::js", kind, "{}", msg);
|
|
||||||
}
|
}
|
||||||
CallbackType::Test {} => {}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cb_result
|
"SelectInstallDir" => {
|
||||||
|
let result =
|
||||||
|
tinyfiledialogs::select_folder_dialog("Select a install directory...", "")
|
||||||
|
.and_then(|v| serde_json::to_value(v).ok());
|
||||||
|
return Some(RpcResponse::new_result(event.id, result));
|
||||||
|
}
|
||||||
|
_ => warn!("Unknown RPC method: {}", event.method),
|
||||||
|
}
|
||||||
|
None
|
||||||
})
|
})
|
||||||
.run()
|
.build()?;
|
||||||
.log_expect("Unable to launch Web UI!");
|
|
||||||
|
event_loop.run(move |event, _, control_flow| {
|
||||||
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::NewEvents(StartCause::Init) => info!("Webview started"),
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => *control_flow = ControlFlow::Exit,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#![deny(unsafe_code)]
|
#![deny(unsafe_code)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
extern crate web_view;
|
extern crate wry;
|
||||||
|
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
extern crate hyper;
|
extern crate hyper;
|
||||||
|
@ -25,13 +25,8 @@ export const i18n = new VueI18n({
|
|||||||
function intercept (method) {
|
function intercept (method) {
|
||||||
console[method] = function () {
|
console[method] = function () {
|
||||||
const message = Array.prototype.slice.apply(arguments).join(' ')
|
const message = Array.prototype.slice.apply(arguments).join(' ')
|
||||||
window.external.invoke(
|
window.rpc.notify(
|
||||||
JSON.stringify({
|
'Log', method, message
|
||||||
Log: {
|
|
||||||
kind: method,
|
|
||||||
msg: message
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,9 +34,7 @@ function intercept (method) {
|
|||||||
// See if we have access to the JSON interface
|
// See if we have access to the JSON interface
|
||||||
let hasExternalInterface = false
|
let hasExternalInterface = false
|
||||||
try {
|
try {
|
||||||
window.external.invoke(JSON.stringify({
|
window.rpc.notify('Test')
|
||||||
Test: {}
|
|
||||||
}))
|
|
||||||
hasExternalInterface = true
|
hasExternalInterface = true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Running without JSON interface - unexpected behaviour may occur!')
|
console.warn('Running without JSON interface - unexpected behaviour may occur!')
|
||||||
@ -50,13 +43,8 @@ try {
|
|||||||
// Overwrite loggers with the logging backend
|
// Overwrite loggers with the logging backend
|
||||||
if (hasExternalInterface) {
|
if (hasExternalInterface) {
|
||||||
window.onerror = function (msg, url, line) {
|
window.onerror = function (msg, url, line) {
|
||||||
window.external.invoke(
|
window.rpc.notify(
|
||||||
JSON.stringify({
|
'Log', 'error', msg + ' @ ' + url + ':' + line
|
||||||
Log: {
|
|
||||||
kind: 'error',
|
|
||||||
msg: msg + ' @ ' + url + ':' + line
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,12 +79,6 @@ axios.get('/api/attrs').then(function (resp) {
|
|||||||
console.error(err)
|
console.error(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
function selectFileCallback (name) {
|
|
||||||
app.install_location = name
|
|
||||||
}
|
|
||||||
|
|
||||||
window.selectFileCallback = selectFileCallback
|
|
||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
i18n: i18n,
|
i18n: i18n,
|
||||||
router: router,
|
router: router,
|
||||||
|
@ -80,7 +80,7 @@ export default {
|
|||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
publicPath: process.env.BASE_URL,
|
publicPath: process.env.BASE_URL,
|
||||||
advanced: false,
|
advanced: true,
|
||||||
repair: false,
|
repair: false,
|
||||||
installDesktopShortcut: true
|
installDesktopShortcut: true
|
||||||
}
|
}
|
||||||
@ -99,11 +99,12 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
select_file: function () {
|
select_file: function () {
|
||||||
window.external.invoke(JSON.stringify({
|
const that = this
|
||||||
SelectInstallDir: {
|
window.rpc.call('SelectInstallDir').then(function (name) {
|
||||||
callback_name: 'selectFileCallback'
|
if (name) {
|
||||||
|
that.$root.$data.install_location = name
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
},
|
},
|
||||||
show_overwrite_dialog: function (confirmCallback) {
|
show_overwrite_dialog: function (confirmCallback) {
|
||||||
this.$buefy.dialog.confirm({
|
this.$buefy.dialog.confirm({
|
||||||
|
Loading…
Reference in New Issue
Block a user