Tweak and organize devtools crate
This commit is contained in:
parent
267583c2a9
commit
ef16ea3b2a
8 changed files with 239 additions and 132 deletions
|
|
@ -31,6 +31,7 @@ impl text::Renderer for () {
|
|||
type Paragraph = ();
|
||||
type Editor = ();
|
||||
|
||||
const MONOSPACE_FONT: Font = Font::MONOSPACE;
|
||||
const ICON_FONT: Font = Font::DEFAULT;
|
||||
const CHECKMARK_ICON: char = '0';
|
||||
const ARROW_DOWN_ICON: char = '0';
|
||||
|
|
|
|||
|
|
@ -232,6 +232,11 @@ pub trait Renderer: crate::Renderer {
|
|||
/// The [`Editor`] of this [`Renderer`].
|
||||
type Editor: Editor<Font = Self::Font> + 'static;
|
||||
|
||||
/// A monospace font.
|
||||
///
|
||||
/// It may be used by devtools.
|
||||
const MONOSPACE_FONT: Self::Font;
|
||||
|
||||
/// The icon font of the backend.
|
||||
const ICON_FONT: Self::Font;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub use palette::Palette;
|
|||
|
||||
use crate::Color;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
|
@ -87,14 +88,17 @@ impl Theme {
|
|||
];
|
||||
|
||||
/// Creates a new custom [`Theme`] from the given [`Palette`].
|
||||
pub fn custom(name: String, palette: Palette) -> Self {
|
||||
pub fn custom(
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
palette: Palette,
|
||||
) -> Self {
|
||||
Self::custom_with_fn(name, palette, palette::Extended::generate)
|
||||
}
|
||||
|
||||
/// Creates a new custom [`Theme`] from the given [`Palette`], with
|
||||
/// a custom generator of a [`palette::Extended`].
|
||||
pub fn custom_with_fn(
|
||||
name: String,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
palette: Palette,
|
||||
generate: impl FnOnce(Palette) -> palette::Extended,
|
||||
) -> Self {
|
||||
|
|
@ -220,7 +224,7 @@ impl fmt::Display for Theme {
|
|||
/// A [`Theme`] with a customized [`Palette`].
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Custom {
|
||||
name: String,
|
||||
name: Cow<'static, str>,
|
||||
palette: Palette,
|
||||
extended: palette::Extended,
|
||||
}
|
||||
|
|
@ -234,12 +238,12 @@ impl Custom {
|
|||
/// Creates a [`Custom`] theme from the given [`Palette`] with
|
||||
/// a custom generator of a [`palette::Extended`].
|
||||
pub fn with_fn(
|
||||
name: String,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
palette: Palette,
|
||||
generate: impl FnOnce(Palette) -> palette::Extended,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
name: name.into(),
|
||||
palette,
|
||||
extended: generate(palette),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
use crate::executor;
|
||||
use crate::runtime::Task;
|
||||
|
||||
use std::io;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub const COMPATIBLE_REVISION: &str =
|
||||
"69dd2283886dccdaa1ee6e1c274af62f7250bc38";
|
||||
|
||||
pub fn launch() -> Task<Result<(), Error>> {
|
||||
pub fn launch() -> Task<launch::Result> {
|
||||
executor::try_spawn_blocking(|mut sender| {
|
||||
let cargo_install = process::Command::new("cargo")
|
||||
.args(["install", "--list"])
|
||||
|
|
@ -22,15 +20,15 @@ pub fn launch() -> Task<Result<(), Error>> {
|
|||
}
|
||||
|
||||
let Some((_, revision)) = line.rsplit_once("?rev=") else {
|
||||
return Err(Error::Outdated { revision: None });
|
||||
return Err(launch::Error::Outdated { revision: None });
|
||||
};
|
||||
|
||||
let Some((revision, _)) = revision.rsplit_once("#") else {
|
||||
return Err(Error::Outdated { revision: None });
|
||||
return Err(launch::Error::Outdated { revision: None });
|
||||
};
|
||||
|
||||
if revision != COMPATIBLE_REVISION {
|
||||
return Err(Error::Outdated {
|
||||
return Err(launch::Error::Outdated {
|
||||
revision: Some(revision.to_owned()),
|
||||
});
|
||||
}
|
||||
|
|
@ -45,16 +43,16 @@ pub fn launch() -> Task<Result<(), Error>> {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
Err(Error::NotFound)
|
||||
Err(launch::Error::NotFound)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn install() -> Task<Result<Installation, Error>> {
|
||||
pub fn install() -> Task<install::Result> {
|
||||
executor::try_spawn_blocking(|mut sender| {
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
let install = Command::new("cargo")
|
||||
let mut install = Command::new("cargo")
|
||||
.args([
|
||||
"install",
|
||||
"--locked",
|
||||
|
|
@ -68,41 +66,75 @@ pub fn install() -> Task<Result<Installation, Error>> {
|
|||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let mut stderr =
|
||||
BufReader::new(install.stderr.expect("stderr must be piped"));
|
||||
let mut stderr = BufReader::new(
|
||||
install.stderr.take().expect("stderr must be piped"),
|
||||
);
|
||||
|
||||
let mut log = String::new();
|
||||
|
||||
while let Ok(n) = stderr.read_line(&mut log) {
|
||||
if n == 0 {
|
||||
break;
|
||||
let status = install.wait()?;
|
||||
|
||||
if status.success() {
|
||||
break;
|
||||
} else {
|
||||
return Err(install::Error::ProcessFailed(status));
|
||||
}
|
||||
}
|
||||
|
||||
let _ = sender.try_send(Installation::Logged(log.clone()));
|
||||
let _ = sender.try_send(install::Event::Logged(log.clone()));
|
||||
log.clear();
|
||||
}
|
||||
|
||||
let _ = sender.try_send(Installation::Finished);
|
||||
let _ = sender.try_send(install::Event::Finished);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Installation {
|
||||
Logged(String),
|
||||
Finished,
|
||||
}
|
||||
pub mod launch {
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
NotFound,
|
||||
Outdated { revision: Option<String> },
|
||||
IoFailed(Arc<io::Error>),
|
||||
}
|
||||
pub type Result = std::result::Result<(), Error>;
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Self::IoFailed(Arc::new(error))
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
NotFound,
|
||||
Outdated { revision: Option<String> },
|
||||
IoFailed(Arc<io::Error>),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Self::IoFailed(Arc::new(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod install {
|
||||
use std::io;
|
||||
use std::process;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type Result = std::result::Result<Event, Error>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
Logged(String),
|
||||
Finished,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
ProcessFailed(process::ExitStatus),
|
||||
IoFailed(Arc<io::Error>),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Self::IoFailed(Arc::new(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,12 @@ mod comet;
|
|||
mod executor;
|
||||
mod time_machine;
|
||||
|
||||
use crate::core::border;
|
||||
use crate::core::keyboard;
|
||||
use crate::core::theme::{self, Base, Theme};
|
||||
use crate::core::time::seconds;
|
||||
use crate::core::window;
|
||||
use crate::core::{Color, Element, Length::Fill};
|
||||
use crate::core::{Alignment::Center, Color, Element, Length::Fill};
|
||||
use crate::futures::Subscription;
|
||||
use crate::program::Program;
|
||||
use crate::runtime::Task;
|
||||
|
|
@ -124,9 +125,9 @@ where
|
|||
enum Message {
|
||||
HideNotification,
|
||||
ToggleComet,
|
||||
CometLaunched(Result<(), comet::Error>),
|
||||
CometLaunched(comet::launch::Result),
|
||||
InstallComet,
|
||||
InstallationProgressed(Result<comet::Installation, comet::Error>),
|
||||
Installing(comet::install::Result),
|
||||
CancelSetup,
|
||||
}
|
||||
|
||||
|
|
@ -195,17 +196,17 @@ where
|
|||
Message::CometLaunched(Ok(())) => Task::none(),
|
||||
Message::CometLaunched(Err(error)) => {
|
||||
match error {
|
||||
comet::Error::NotFound => {
|
||||
comet::launch::Error::NotFound => {
|
||||
self.mode = Mode::Setup(Setup::Idle {
|
||||
goal: Goal::Installation,
|
||||
});
|
||||
}
|
||||
comet::Error::Outdated { revision } => {
|
||||
comet::launch::Error::Outdated { revision } => {
|
||||
self.mode = Mode::Setup(Setup::Idle {
|
||||
goal: Goal::Update { revision },
|
||||
});
|
||||
}
|
||||
comet::Error::IoFailed(error) => {
|
||||
comet::launch::Error::IoFailed(error) => {
|
||||
log::error!("comet failed to run: {error}");
|
||||
}
|
||||
}
|
||||
|
|
@ -217,29 +218,42 @@ where
|
|||
Mode::Setup(Setup::Running { logs: Vec::new() });
|
||||
|
||||
comet::install()
|
||||
.map(Message::InstallationProgressed)
|
||||
.map(Message::Installing)
|
||||
.map(Event::Message)
|
||||
}
|
||||
|
||||
Message::InstallationProgressed(Ok(installation)) => {
|
||||
Message::Installing(Ok(installation)) => {
|
||||
let Mode::Setup(Setup::Running { logs }) = &mut self.mode
|
||||
else {
|
||||
return Task::none();
|
||||
};
|
||||
|
||||
match installation {
|
||||
comet::Installation::Logged(log) => {
|
||||
comet::install::Event::Logged(log) => {
|
||||
logs.push(log);
|
||||
Task::none()
|
||||
}
|
||||
comet::Installation::Finished => {
|
||||
comet::install::Event::Finished => {
|
||||
self.mode = Mode::None;
|
||||
comet::launch().discard()
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::InstallationProgressed(_error) => {
|
||||
// TODO
|
||||
Message::Installing(Err(error)) => {
|
||||
let Mode::Setup(Setup::Running { logs }) = &mut self.mode
|
||||
else {
|
||||
return Task::none();
|
||||
};
|
||||
|
||||
match error {
|
||||
comet::install::Error::ProcessFailed(status) => {
|
||||
logs.push(format!("process failed with {status}"));
|
||||
}
|
||||
comet::install::Error::IoFailed(error) => {
|
||||
logs.push(error.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Task::none()
|
||||
}
|
||||
Message::CancelSetup => {
|
||||
|
|
@ -304,7 +318,7 @@ where
|
|||
let derive_theme = move || {
|
||||
theme
|
||||
.palette()
|
||||
.map(|palette| Theme::custom("DevTools".to_owned(), palette))
|
||||
.map(|palette| Theme::custom("iced devtools", palette))
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
|
|
@ -312,97 +326,14 @@ where
|
|||
Mode::None => None,
|
||||
Mode::Setup(setup) => {
|
||||
let stage: Element<'_, _, Theme, P::Renderer> = match setup {
|
||||
Setup::Idle { goal } => {
|
||||
let controls = row![
|
||||
button(text("Cancel").center().width(Fill))
|
||||
.width(100)
|
||||
.on_press(Message::CancelSetup)
|
||||
.style(button::danger),
|
||||
horizontal_space(),
|
||||
button(
|
||||
text(match goal {
|
||||
Goal::Installation => "Install",
|
||||
Goal::Update { .. } => "Update",
|
||||
})
|
||||
.center()
|
||||
.width(Fill)
|
||||
)
|
||||
.width(100)
|
||||
.on_press(Message::InstallComet)
|
||||
.style(button::success),
|
||||
];
|
||||
|
||||
let command = container(
|
||||
text(
|
||||
"cargo install --locked \
|
||||
--git https://github.com/iced-rs/comet.git",
|
||||
)
|
||||
.size(14),
|
||||
)
|
||||
.width(Fill)
|
||||
.padding(5)
|
||||
.style(container::dark);
|
||||
|
||||
match goal {
|
||||
Goal::Installation => column![
|
||||
text("comet is not installed!").size(20),
|
||||
"In order to display performance \
|
||||
metrics, the comet debugger must \
|
||||
be installed in your system.",
|
||||
"The comet debugger is an official \
|
||||
companion tool that helps you debug \
|
||||
your iced applications.",
|
||||
"Do you wish to install it with the \
|
||||
following command?",
|
||||
command,
|
||||
controls,
|
||||
]
|
||||
.spacing(20),
|
||||
Goal::Update { revision } => column![
|
||||
text("comet is out of date!").size(20),
|
||||
text!(
|
||||
"The installed revision is \"{current}\", \
|
||||
but the latest compatible is \"{compatible}\".",
|
||||
current = revision
|
||||
.as_deref()
|
||||
.unwrap_or("Unknown"),
|
||||
compatible = comet::COMPATIBLE_REVISION,
|
||||
),
|
||||
"Do you wish to update it with the following \
|
||||
command?",
|
||||
command,
|
||||
controls,
|
||||
]
|
||||
.spacing(20),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
Setup::Running { logs } => column![
|
||||
text("Installing comet...").size(20),
|
||||
container(
|
||||
scrollable(
|
||||
column(
|
||||
logs.iter()
|
||||
.map(|log| text(log).size(12).into()),
|
||||
)
|
||||
.spacing(3),
|
||||
)
|
||||
.spacing(10)
|
||||
.width(Fill)
|
||||
.height(300)
|
||||
.anchor_bottom(),
|
||||
)
|
||||
.padding(10)
|
||||
.style(container::dark)
|
||||
]
|
||||
.spacing(20)
|
||||
.into(),
|
||||
Setup::Idle { goal } => self::setup(goal),
|
||||
Setup::Running { logs } => installation(logs),
|
||||
};
|
||||
|
||||
let setup = center(
|
||||
container(stage)
|
||||
.padding(20)
|
||||
.width(500)
|
||||
.max_width(500)
|
||||
.style(container::bordered_box),
|
||||
)
|
||||
.padding(10)
|
||||
|
|
@ -510,3 +441,134 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup<Renderer>(goal: &Goal) -> Element<'_, Message, Theme, Renderer>
|
||||
where
|
||||
Renderer: core::text::Renderer + 'static,
|
||||
{
|
||||
let controls = row![
|
||||
button(text("Cancel").center().width(Fill))
|
||||
.width(100)
|
||||
.on_press(Message::CancelSetup)
|
||||
.style(button::danger),
|
||||
horizontal_space(),
|
||||
button(
|
||||
text(match goal {
|
||||
Goal::Installation => "Install",
|
||||
Goal::Update { .. } => "Update",
|
||||
})
|
||||
.center()
|
||||
.width(Fill)
|
||||
)
|
||||
.width(100)
|
||||
.on_press(Message::InstallComet)
|
||||
.style(button::success),
|
||||
];
|
||||
|
||||
let command = container(
|
||||
text!(
|
||||
"cargo install --locked \\
|
||||
--git https://github.com/iced-rs/comet.git \\
|
||||
--rev {}",
|
||||
comet::COMPATIBLE_REVISION
|
||||
)
|
||||
.size(14)
|
||||
.font(Renderer::MONOSPACE_FONT),
|
||||
)
|
||||
.width(Fill)
|
||||
.padding(5)
|
||||
.style(container::dark);
|
||||
|
||||
Element::from(match goal {
|
||||
Goal::Installation => column![
|
||||
text("comet is not installed!").size(20),
|
||||
"In order to display performance \
|
||||
metrics, the comet debugger must \
|
||||
be installed in your system.",
|
||||
"The comet debugger is an official \
|
||||
companion tool that helps you debug \
|
||||
your iced applications.",
|
||||
column![
|
||||
"Do you wish to install it with the \
|
||||
following command?",
|
||||
command
|
||||
]
|
||||
.spacing(10),
|
||||
controls,
|
||||
]
|
||||
.spacing(20),
|
||||
Goal::Update { revision } => {
|
||||
let comparison = column![
|
||||
row![
|
||||
"Installed revision:",
|
||||
horizontal_space(),
|
||||
inline_code(revision.as_deref().unwrap_or("Unknown"))
|
||||
]
|
||||
.align_y(Center),
|
||||
row![
|
||||
"Compatible revision:",
|
||||
horizontal_space(),
|
||||
inline_code(comet::COMPATIBLE_REVISION),
|
||||
]
|
||||
.align_y(Center)
|
||||
]
|
||||
.spacing(5);
|
||||
|
||||
column![
|
||||
text("comet is out of date!").size(20),
|
||||
comparison,
|
||||
column![
|
||||
"Do you wish to update it with the following \
|
||||
command?",
|
||||
command
|
||||
]
|
||||
.spacing(10),
|
||||
controls,
|
||||
]
|
||||
.spacing(20)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn installation<'a, Renderer>(
|
||||
logs: &'a [String],
|
||||
) -> Element<'a, Message, Theme, Renderer>
|
||||
where
|
||||
Renderer: core::text::Renderer + 'a,
|
||||
{
|
||||
column![
|
||||
text("Installing comet...").size(20),
|
||||
container(
|
||||
scrollable(
|
||||
column(logs.iter().map(|log| {
|
||||
text(log).size(12).font(Renderer::MONOSPACE_FONT).into()
|
||||
}),)
|
||||
.spacing(3),
|
||||
)
|
||||
.spacing(10)
|
||||
.width(Fill)
|
||||
.height(300)
|
||||
.anchor_bottom(),
|
||||
)
|
||||
.padding(10)
|
||||
.style(container::dark)
|
||||
]
|
||||
.spacing(20)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn inline_code<'a, Renderer>(
|
||||
code: impl text::IntoFragment<'a>,
|
||||
) -> Element<'a, Message, Theme, Renderer>
|
||||
where
|
||||
Renderer: core::text::Renderer + 'a,
|
||||
{
|
||||
container(text(code).font(Renderer::MONOSPACE_FONT).size(12))
|
||||
.style(|_theme| {
|
||||
container::Style::default()
|
||||
.background(Color::BLACK)
|
||||
.border(border::rounded(2))
|
||||
})
|
||||
.padding([2, 4])
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ where
|
|||
type Paragraph = A::Paragraph;
|
||||
type Editor = A::Editor;
|
||||
|
||||
const MONOSPACE_FONT: Self::Font = A::MONOSPACE_FONT;
|
||||
const ICON_FONT: Self::Font = A::ICON_FONT;
|
||||
const CHECKMARK_ICON: char = A::CHECKMARK_ICON;
|
||||
const ARROW_DOWN_ICON: char = A::ARROW_DOWN_ICON;
|
||||
|
|
|
|||
|
|
@ -235,6 +235,7 @@ impl core::text::Renderer for Renderer {
|
|||
type Paragraph = Paragraph;
|
||||
type Editor = Editor;
|
||||
|
||||
const MONOSPACE_FONT: Font = Font::MONOSPACE;
|
||||
const ICON_FONT: Font = Font::with_name("Iced-Icons");
|
||||
const CHECKMARK_ICON: char = '\u{f00c}';
|
||||
const ARROW_DOWN_ICON: char = '\u{e800}';
|
||||
|
|
|
|||
|
|
@ -643,6 +643,7 @@ impl core::text::Renderer for Renderer {
|
|||
type Paragraph = Paragraph;
|
||||
type Editor = Editor;
|
||||
|
||||
const MONOSPACE_FONT: Font = Font::MONOSPACE;
|
||||
const ICON_FONT: Font = Font::with_name("Iced-Icons");
|
||||
const CHECKMARK_ICON: char = '\u{f00c}';
|
||||
const ARROW_DOWN_ICON: char = '\u{e800}';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue