Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ members = [
resolver = "2"

[profile.release.package.futuremod_hook]
opt-level = 0 # Completely disable optimizations for dll due to issues with raw memory modification
opt-level = 0 # Completely disable optimizations for dll due to issues with raw memory modification
10 changes: 10 additions & 0 deletions examples/settings/info.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name = "Settings Test"
version = "0.0.1"
authors = ["Simon Kurz"]
dependencies = ["settings", "system"]
description = """\
# Settings Test
This plugin tests the settings function.

It does nothing more than registering some settings and logging changes to them.
"""
24 changes: 24 additions & 0 deletions examples/settings/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
local settings = require("settings")

function onLoad()
createSettings()

print(`Creation took {diff} ms`)
end

function createSettings()
settings:create({
settings.Section({
settings.Text("Example Settings"),
settings.Text("By clicking the button below, you should be able to trigger some code of the plugin. How neat!!!"),
settings.Button("Click Me"):onClick(function()
print("Clicked")
end)
:withID("button")
}),
settings.Section({
settings.Text("Next Section"),
settings.Button("Other butotn"):withDisabled(true)
})
})
end
64 changes: 62 additions & 2 deletions futuremod/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use crate::config;
use anyhow::{anyhow, bail};
use log::info;
use reqwest::Body;
use serde::de::DeserializeOwned;
use serde::{de::DeserializeOwned, Serialize};
use tokio::fs;
use tokio_util::codec::{BytesCodec, FramedRead};

use futuremod_data::plugin::{Plugin, PluginInfo};
use futuremod_data::plugin::{settings::{Event, PluginSettings, SettingsEvent}, Plugin, PluginInfo};

pub fn build_url(path: &str) -> String {
let config = config::get();
Expand Down Expand Up @@ -236,3 +236,63 @@ pub async fn disable_plugin(name: String) -> Result<(), anyhow::Error> {

Ok(())
}

#[derive(Debug, Clone, Serialize)]
pub struct PluginByName {
pub name: String,
}


pub async fn get_plugin_settings(name: String) -> Result<PluginSettings, anyhow::Error> {
let body = PluginByName {
name,
};

let response = reqwest::Client::new()
.put(build_url("/plugin/settings"))
.json(&body)
.send()
.await
.map_err(|e| anyhow!("Could not send request to get settings: {}", e))?;

if !response.status().is_success() {
let response_text = response
.text()
.await
.map_err(|e| anyhow!("Could nto get response content: {}", e))?;

bail!("{}", response_text);
}

response
.json()
.await
.map_err(|e| anyhow!("Plugin settings has unexpected format: {}", e))
}

pub async fn send_settings_event(name: String, id: String, event: Event) -> Result<(), anyhow::Error> {
let body = SettingsEvent {
name,
event,
target_id: id,
};

let response = reqwest::Client::new()
.put(build_url("/plugin/settings/event"))
.json(&body)
.send()
.await
.map_err(|e| anyhow!("Could not send request for settings event: {}", e))?;

if !response.status().is_success() {
let response_text = response
.text()
.await

.map_err(|e| anyhow!("Could not get response content: {}", e))?;

bail!("{}", response_text);
}

Ok(())
}
17 changes: 12 additions & 5 deletions futuremod/src/view/dashboard/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,9 @@ pub fn update(dashboard: &mut Dashboard, message: Message) -> Task<Message> {
let plugin = dashboard.plugins.get(&name);
match plugin {
Some(plugin) => {
dashboard.view = View::Plugin(view::plugin::Plugin::new(plugin));
let (view, message) = view::plugin::Plugin::new(plugin);
dashboard.view = View::Plugin(view);
return message.map(Message::Plugin);
}
None => {}
}
Expand Down Expand Up @@ -290,12 +292,17 @@ pub fn update(dashboard: &mut Dashboard, message: Message) -> Task<Message> {
},
_ => (),
},
View::Plugin(_) => match message {
View::Plugin(plugin_view) => match message {
Message::Plugin(plugin_message) => match plugin_message {
view::plugin::Message::GoBack => {
return Task::done(Message::ToPluginList);
view::plugin::Message::GoBack => return Task::done(Message::ToPluginList),
plugin_message => {
if let Some(plugin) = dashboard.plugins.get(&plugin_view.name) {
return plugin_view.update(plugin, plugin_message)
.map(Message::Plugin);
} else {
warn!("Plugin view active but plugin '{}' not found", plugin_view.name);
}
}
_ => (),
},
_ => (),
},
Expand Down
55 changes: 52 additions & 3 deletions futuremod/src/view/plugin/components.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use futuremod_data::plugin::{Plugin, PluginDependency, PluginState};
use futuremod_data::plugin::{settings::{self, Component, PluginSettings}, Plugin, PluginDependency, PluginState};
use iced::{
widget::{column, container, markdown, row, text, Scrollable, Toggler},
widget::{column, container, markdown, row, scrollable, text, Scrollable, Toggler},
Alignment, Length, Padding,
};
use iced_fonts::Bootstrap;
Expand All @@ -14,7 +14,7 @@ use crate::{
},
};

use super::Message;
use super::{view::FutureResult, Message};

fn plugin_reload_button<'a>(plugin: &Plugin) -> Element<'a, Message> {
icon_text_button(Bootstrap::ArrowClockwise, "Reload")
Expand Down Expand Up @@ -111,6 +111,7 @@ pub fn plugin_details_view<'a>(
])
.padding(8),
plugin_details_content(&plugin_view.description, plugin),
plugin_info_box(loading_settings(&plugin_view.settings)),
]
.into()
}
Expand Down Expand Up @@ -207,3 +208,51 @@ fn plugin_toggle_button<'a>(plugin: &Plugin) -> Option<Element<'a, Message>> {
.into(),
)
}

fn loading_settings<'a>(loading_settings: &'a FutureResult<PluginSettings, String>) -> Element<'a, Message> {
match loading_settings {
FutureResult::Loading => text("Loading...").into(),
FutureResult::Finished(plugin_settings) => settings(plugin_settings),
FutureResult::Error(e) => text(format!("Could not load plugin settings: {}", e)).into(),
}
}

fn settings<'a>(settings: &'a PluginSettings) -> Element<'a, Message> {
scrollable(render_component(&settings.root)).into()
}

fn render_component<'a>(component: &'a settings::Component) -> Element<'a, Message> {
match component {
Component::Button(button) => render_button(button),
Component::Section(section) => render_section(section),
Component::Text(text) => render_text(text),
}
}

fn render_button<'a>(settings_button: &'a settings::Button) -> Element<'a, Message> {
button(text(&settings_button.text))
.on_press_maybe(if settings_button.disabled {
None
} else {
Some(Message::SettingsEvent(settings_button.id.clone(), settings::Event::ClickButton))
})
.into()
}

fn render_section<'a>(settings_section: &'a settings::Section) -> Element<'a, Message> {
let mut section = Column::new();

for component in settings_section.content.iter() {
section = section.push(render_component(&component));
}

section
.spacing(8)
.padding(Padding{top: 16.0, right: 0.0, bottom: 16.0, left: 0.0})
.into()
}

fn render_text<'a>(settings_text: &'a settings::Text) -> Element<'a, Message> {
text(&settings_text.text)
.into()
}
1 change: 1 addition & 0 deletions futuremod/src/view/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod components;
mod view;
mod state;

pub use view::{Message, Plugin};
25 changes: 25 additions & 0 deletions futuremod/src/view/plugin/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use futures::TryFutureExt;
use iced::Task;
use log::info;

use crate::api::send_settings_event;

use super::{view::FutureResult, Message};

pub(super) fn update(view: &mut super::Plugin, plugin: &futuremod_data::plugin::Plugin, message: Message) -> Task<Message> {
match message {
Message::HandlePluginSettingsResponse(result) => match result {
Ok(settings) => view.settings = FutureResult::Finished(settings),
Err(e) => view.settings = FutureResult::Error(e),
},
Message::SettingsEvent(id, event) => {
return Task::perform(send_settings_event(plugin.info.name.clone(), id, event).map_err(|e| e.to_string()), Message::EventResponse);
},
Message::EventResponse(response) => {
info!("Event response: {:?}", response);
}
_ => (),
}

Task::none()
}
37 changes: 29 additions & 8 deletions futuremod/src/view/plugin/view.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
use iced::widget::markdown;
use futuremod_data::plugin::settings::{Event, PluginSettings};
use futures::TryFutureExt;
use iced::{widget::markdown, Task};

use crate::widget::Element;
use crate::{api::get_plugin_settings, widget::Element};

use super::components::plugin_details_view;
use super::{components::plugin_details_view, state::update};

#[derive(Debug, Clone)]
pub enum FutureResult<T, E> {
Loading,
Finished(T),
Error(E),
}

#[derive(Debug, Clone)]
pub struct Plugin {
pub name: String,
pub description: Vec<markdown::Item>,
pub settings: FutureResult<PluginSettings, String>,
}

#[derive(Debug, Clone)]
Expand All @@ -18,16 +28,27 @@ pub enum Message {
Reload(String),
UninstallPrompt(String),
OpenUrl(reqwest::Url),
HandlePluginSettingsResponse(Result<PluginSettings, String>),
SettingsEvent(String, Event),
EventResponse(Result<(), String>),
}

impl Plugin {
pub fn new(plugin: &futuremod_data::plugin::Plugin) -> Self {
pub fn new(plugin: &futuremod_data::plugin::Plugin) -> (Self, Task<Message>) {
let description = markdown::parse(&plugin.info.description).collect();

Plugin {
name: plugin.info.name.clone(),
description,
}
(
Plugin {
name: plugin.info.name.clone(),
description,
settings: FutureResult::Loading,
},
Task::perform(get_plugin_settings(plugin.info.name.clone()).map_err(|e| e.to_string()), Message::HandlePluginSettingsResponse),
)
}

pub fn update(&mut self, plugin: &futuremod_data::plugin::Plugin, message: Message) -> Task<Message> {
update(self, plugin, message)
}

pub fn view<'a>(&'a self, plugin: &futuremod_data::plugin::Plugin) -> Element<'a, Message> {
Expand Down
4 changes: 4 additions & 0 deletions futuremod_data/src/plugin/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod plugin;
pub mod settings;

pub use plugin::*;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub enum PluginDependency {
UI,
System,
Matrix,
Settings,

// The following libraries are from the standard library
Math,
Expand All @@ -35,6 +36,7 @@ impl Display for PluginDependency {
PluginDependency::String => f.write_str("String"),
PluginDependency::Utf8 => f.write_str("Utf8"),
PluginDependency::Matrix => f.write_str("Matrix"),
PluginDependency::Settings => f.write_str("Settings"),
}
}
}
Expand Down
Loading