~yiwang/localnative

6b9f0b6a078b054bbcab4d274944c9f81d91e255 — Cupnfish a month ago c66c7a3 + 4b8db7a
Merge branch 'feat--add-logger-to-display-op-info' into 'master'

Feat:  add logger to display op info

See merge request localnative/localnative!9
M localnative-rs/localnative_core/Cargo.toml => localnative-rs/localnative_core/Cargo.toml +2 -0
@@ 40,3 40,5 @@ name = "localnative_core"
# crate-type = ["cdylib"]
# ios works as below
# crate-type = ["staticlib"]
# desktop release will need this
# crate-type = ["rlib"]

M localnative-rs/localnative_iced/Cargo.toml => localnative-rs/localnative_iced/Cargo.toml +1 -1
@@ 20,7 20,7 @@ iced_native = { git = "https://github.com/hecrj/iced.git" }
iced_graphics = { git = "https://github.com/hecrj/iced.git" }
font-kit = "0.10.0"
once_cell = "1.7"
log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] }
log = { version = "0.4", features = ["max_level_debug", "release_max_level_info"] }
fern = "0.6"
uuid = { version = "0.8", features = ["v4"] }
image = "0.23"

A localnative-rs/localnative_iced/src/logger.rs => localnative-rs/localnative_iced/src/logger.rs +197 -0
@@ 0,0 1,197 @@
use std::sync::mpsc::Receiver;

use iced_native::{
    event, layout, Clipboard, Color, Event, Hasher, HorizontalAlignment, Layout, Length, Point,
    Rectangle, Size, VerticalAlignment, Widget,
};

use std::hash::Hash;

#[derive(Debug)]
pub struct Logger {
    state: State,
}

impl Logger {
    pub fn new<T: Into<String>>(record: Receiver<String>, label: T) -> Self {
        Self {
            state: State::new(record, label),
        }
    }
    pub fn state(&mut self) -> &mut State {
        &mut self.state
    }
}
#[derive(Debug)]
pub struct State {
    record: Receiver<String>,
    content: String,
}
impl State {
    pub fn new<T: Into<String>>(record: Receiver<String>, content: T) -> Self {
        Self {
            record,
            content: content.into(),
        }
    }
}
#[derive(Debug)]
pub struct Record<'a, Renderer>
where
    Renderer: iced_native::text::Renderer,
{
    state: &'a mut State,
    size: Option<u16>,
    color: Option<Color>,
    font: Renderer::Font,
    width: Length,
    height: Length,
    horizontal_alignment: HorizontalAlignment,
    vertical_alignment: VerticalAlignment,
}

impl<'a, Renderer> Record<'a, Renderer>
where
    Renderer: iced_native::text::Renderer,
{
    /// Create a new fragment of [`Record`] with the given contents.
    pub fn new(state: &'a mut State) -> Self {
        Self {
            state,
            size: None,
            color: None,
            font: Default::default(),
            width: Length::Shrink,
            height: Length::Shrink,
            horizontal_alignment: HorizontalAlignment::Left,
            vertical_alignment: VerticalAlignment::Top,
        }
    }

    /// Sets the size of the [`Record`].
    pub fn size(mut self, size: u16) -> Self {
        self.size = Some(size);
        self
    }

    /// Sets the [`Color`] of the [`Record`].
    pub fn color<C: Into<Color>>(mut self, color: C) -> Self {
        self.color = Some(color.into());
        self
    }

    /// Sets the [`Font`] of the [`Record`].
    ///
    /// [`Font`]: Renderer::Font
    pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
        self.font = font.into();
        self
    }

    /// Sets the width of the [`Record`] boundaries.
    pub fn width(mut self, width: Length) -> Self {
        self.width = width;
        self
    }

    /// Sets the height of the [`Record`] boundaries.
    pub fn height(mut self, height: Length) -> Self {
        self.height = height;
        self
    }

    /// Sets the [`HorizontalAlignment`] of the [`Record`].
    pub fn horizontal_alignment(mut self, alignment: HorizontalAlignment) -> Self {
        self.horizontal_alignment = alignment;
        self
    }

    /// Sets the [`VerticalAlignment`] of the [`Record`].
    pub fn vertical_alignment(mut self, alignment: VerticalAlignment) -> Self {
        self.vertical_alignment = alignment;
        self
    }
}

impl<'a, Renderer, Message> Widget<Message, Renderer> for Record<'a, Renderer>
where
    Renderer: iced_native::text::Renderer,
{
    fn width(&self) -> Length {
        self.width
    }

    fn height(&self) -> Length {
        self.height
    }

    fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
        let limits = limits.width(self.width).height(self.height);

        let size = self.size.unwrap_or(renderer.default_size());

        let bounds = limits.max();

        let (width, height) = renderer.measure(&self.state.content, size, self.font, bounds);

        let size = limits.resolve(Size::new(width, height));

        layout::Node::new(size)
    }

    fn draw(
        &self,
        renderer: &mut Renderer,
        defaults: &Renderer::Defaults,
        layout: Layout<'_>,
        _cursor_position: Point,
        _viewport: &Rectangle,
    ) -> Renderer::Output {
        renderer.draw(
            defaults,
            layout.bounds(),
            &self.state.content,
            self.size.unwrap_or(renderer.default_size()),
            self.font,
            self.color,
            self.horizontal_alignment,
            self.vertical_alignment,
        )
    }

    fn hash_layout(&self, state: &mut Hasher) {
        struct Marker;
        std::any::TypeId::of::<Marker>().hash(state);

        self.state.content.hash(state);
        self.size.hash(state);
        self.width.hash(state);
        self.height.hash(state);
    }
    fn on_event(
        &mut self,
        _event: Event,
        _layout: Layout<'_>,
        _cursor_positionn: Point,
        _renderer: &Renderer,
        _clipboard: &mut dyn Clipboard,
        _messages: &mut Vec<Message>,
    ) -> event::Status {
        if let Ok(rd) = self.state.record.try_recv() {
            self.state.content = rd;
            event::Status::Captured
        } else {
            event::Status::Ignored
        }
    }
}

impl<'a, Renderer, Message> From<Record<'a, Renderer>>
    for iced_native::Element<'a, Message, Renderer>
where
    Renderer: iced_native::text::Renderer + 'a,
{
    fn from(rd: Record<'a, Renderer>) -> Self {
        iced_native::Element::new(rd)
    }
}

M localnative-rs/localnative_iced/src/main.rs => localnative-rs/localnative_iced/src/main.rs +52 -21
@@ 12,6 12,8 @@ mod style;
mod tags;

#[allow(dead_code)]
mod logger;
#[allow(dead_code)]
mod wrap;

use iced::window;


@@ 26,7 28,10 @@ use once_cell::sync::OnceCell;
use page_bar::PageBar;
use search_bar::SearchBar;
use setting_view::{Backend, Config, SettingView};
use std::{path::PathBuf, sync::Arc};
use std::{
    path::PathBuf,
    sync::{mpsc::Sender, Arc},
};
use wrap::Wrap;

pub const BACKEND: &str = "WGPU_BACKEND";


@@ 66,7 71,7 @@ fn main() -> anyhow::Result<()> {
    })
    .map_err(|iced_err| anyhow::anyhow!("iced err:{:?}", iced_err))
}
async fn setup_logger() -> anyhow::Result<()> {
async fn setup_logger(sender: Sender<String>) -> anyhow::Result<()> {
    let dispatch = fern::Dispatch::new().format(|out, message, record| {
        out.finish(format_args!(
            "{}[{}][{}] {}",


@@ 81,13 86,15 @@ async fn setup_logger() -> anyhow::Result<()> {
        tokio::fs::create_dir_all(&log_dir).await?;
    }
    dispatch
        .level(log::LevelFilter::Debug)
        .level(log::LevelFilter::Info)
        .level_for("tracing", log::LevelFilter::Warn)
        .level_for("wgpu_core", log::LevelFilter::Warn)
        .level_for("wgpu_core", log::LevelFilter::Error)
        .level_for("gpu_alloc", log::LevelFilter::Warn)
        .level_for("wgpu", log::LevelFilter::Warn)
        .level_for("iced_wgpu", log::LevelFilter::Warn)
        .level_for("gfx_backend_dx12", log::LevelFilter::Warn)
        .level_for("trapc", log::LevelFilter::Warn)
        .chain(sender)
        .chain(std::io::stdout())
        .chain(fern::log_file(log_dir.join("localnative.log"))?)
        .apply()


@@ 125,7 132,9 @@ fn font() -> &'static Arc<Vec<u8>> {

#[allow(clippy::large_enum_variant)]
enum LocalNative {
    Loading,
    Loading {
        logger: Option<std::sync::mpsc::Receiver<String>>,
    },
    Loaded(Data),
}



@@ 155,21 164,26 @@ impl Application for LocalNative {
    type Flags = bool;

    fn new(is_first: Self::Flags) -> (Self, iced::Command<Self::Message>) {
        let (sender, recevier) = std::sync::mpsc::channel();
        if is_first {
            (
                Self::Loading,
                Self::Loading {
                    logger: Some(recevier),
                },
                Command::batch(vec![
                    Command::perform(setting_view::Config::load(), Message::Loaded),
                    Command::perform(init::init_app_host(), Message::ResultHandle),
                    Command::perform(setup_logger(), Message::ResultHandle),
                    Command::perform(setup_logger(sender), Message::ResultHandle),
                ]),
            )
        } else {
            (
                Self::Loading,
                Self::Loading {
                    logger: Some(recevier),
                },
                Command::batch(vec![
                    Command::perform(setting_view::Config::load(), Message::Loaded),
                    Command::perform(setup_logger(), Message::ResultHandle),
                    Command::perform(setup_logger(sender), Message::ResultHandle),
                ]),
            )
        }


@@ 185,7 199,7 @@ impl Application for LocalNative {
        _clipboard: &mut iced::Clipboard,
    ) -> iced::Command<Self::Message> {
        match self {
            LocalNative::Loading => match message {
            LocalNative::Loading { logger } => match message {
                Message::Loaded(config) => {
                    if let Ok(mut config) = config {
                        let resource = Resource::default();


@@ 206,13 220,26 @@ impl Application for LocalNative {
                        );
                        let mut data_view = DataView::default();
                        middle_data.encode(&mut data_view, &mut page_bar);
                        let data = Data {
                            data_view,
                            resource,
                            setting_view,
                            search_bar,
                            page_bar,
                            ..Default::default()
                        let data = if logger.is_some() {
                            let logger = logger.take().unwrap();
                            Data {
                                data_view,
                                resource,
                                setting_view,
                                search_bar,
                                page_bar,
                                logger: Some(logger::Logger::new(logger, "")),
                                ..Default::default()
                            }
                        } else {
                            Data {
                                data_view,
                                resource,
                                setting_view,
                                search_bar,
                                page_bar,
                                ..Default::default()
                            }
                        };
                        *self = LocalNative::Loaded(data);
                        Command::none()


@@ 503,7 530,7 @@ impl Application for LocalNative {
    }
    fn view(&mut self) -> iced::Element<'_, Self::Message> {
        match self {
            LocalNative::Loading => Container::new(
            LocalNative::Loading { .. } => Container::new(
                Text::new("Loading...")
                    .horizontal_alignment(iced::HorizontalAlignment::Center)
                    .size(50),


@@ 530,6 557,7 @@ pub struct Data {
    page_bar: PageBar,
    server_state: ServerState,
    state: State,
    logger: Option<logger::Logger>,
}
#[derive(Debug)]
pub enum ServerState {


@@ 560,19 588,21 @@ impl Data {
    fn sync_view(&mut self) -> Element<Message> {
        let Data {
            setting_view: config_view,
            logger,
            ..
        } = self;
        config_view
            .sync_board_open_view()
            .sync_board_open_view(logger)
            .map(Message::SettingMessage)
    }
    fn setting_view(&mut self) -> Element<Message> {
        let Data {
            setting_view: config_view,
            logger,
            ..
        } = self;
        config_view
            .setting_board_open_view()
            .setting_board_open_view(logger)
            .map(Message::SettingMessage)
    }
    fn contents_view(&mut self) -> Element<Message> {


@@ 581,6 611,7 @@ impl Data {
            setting_view: config_view,
            search_bar,
            page_bar,
            logger,
            ..
        } = self;
        let DataView { notes, tags, state } = data_view;


@@ 592,7 623,7 @@ impl Data {
        let search_text_is_empty = search_bar.search_text.is_empty();
        Row::new()
            .align_items(iced::Align::Start)
            .push(config_view.viwe().map(Message::SettingMessage))
            .push(config_view.viwe(logger).map(Message::SettingMessage))
            .push(
                iced::Container::new(
                    Column::new()

M localnative-rs/localnative_iced/src/setting_view.rs => localnative-rs/localnative_iced/src/setting_view.rs +34 -12
@@ 7,9 7,12 @@ use iced::{
};
use serde::{Deserialize, Serialize};

use crate::style::{
    symbol::{self, Symbol},
    Theme,
use crate::{
    logger::{Logger, Record},
    style::{
        symbol::{self, Symbol},
        Theme,
    },
};

#[derive(Debug, Clone)]


@@ 29,6 32,7 @@ pub enum Message {
    Reset,
    ClearAddrInput,
    FixHost,
    Empty,
}

#[derive(Debug, Deserialize, Serialize, Clone, Copy)]


@@ 269,31 273,38 @@ impl SettingView {
                }
            }
            Message::FixHost => {}
            Message::Empty => {}
        }
    }
    pub fn viwe(&mut self) -> Element<Message> {
    pub fn viwe<'a>(&'a mut self, logger: &'a mut Option<Logger>) -> Element<'a, Message> {
        let SettingView { config, state, .. } = self;
        left_bar_viwe(state, config.theme)
        left_bar_viwe(state, config.theme, logger)
    }
    pub fn setting_board_open_view(&mut self) -> Element<Message> {
    pub fn setting_board_open_view<'a>(
        &'a mut self,
        logger: &'a mut Option<Logger>,
    ) -> Element<'a, Message> {
        let SettingView {
            config,
            state,
            board_state,
            ..
        } = self;
        let left_bar = left_bar_viwe(state, config.theme);
        let left_bar = left_bar_viwe(state, config.theme, logger);
        let setting_board = setting_board_view(board_state, &*config);
        Row::new().push(left_bar).push(setting_board).into()
    }
    pub fn sync_board_open_view(&mut self) -> Element<Message> {
    pub fn sync_board_open_view<'a>(
        &'a mut self,
        logger: &'a mut Option<Logger>,
    ) -> Element<'a, Message> {
        let SettingView {
            config,
            state,
            sync_state,
            ..
        } = self;
        let left_bar = left_bar_viwe(state, config.theme);
        let left_bar = left_bar_viwe(state, config.theme, logger);
        let sync_board = sync_board_view(&*sync_state);
        Row::new().push(left_bar).push(sync_board).into()
    }


@@ 317,7 328,11 @@ fn sync_board_view(state: &SyncState) -> Element<Message> {
        .push(iced::QRCode::new(qr_code).cell_size(10))
        .into()
}
fn left_bar_viwe(state: &mut State, theme: Theme) -> Element<Message> {
fn left_bar_viwe<'a>(
    state: &'a mut State,
    theme: Theme,
    logger: &'a mut Option<Logger>,
) -> Element<'a, Message> {
    let State {
        theme_button,
        setting_button,


@@ 369,14 384,21 @@ fn left_bar_viwe(state: &mut State, theme: Theme) -> Element<Message> {
    )
    .style(Symbol)
    .on_press(Message::SelectSettingBoard);
    Column::new()

    let mut content = Column::new()
        .align_items(iced::Align::Center)
        .height(iced::Length::Fill)
        .width(iced::Length::FillPortion(2))
        .spacing(10)
        .push(open_file_button)
        .push(server_button)
        .push(addr_row)
        .push(addr_row);
    content = if let Some(ref mut logger) = logger {
        content.push(Record::new(logger.state()))
    } else {
        content
    };
    content
        .push(Rule::vertical(50).style(symbol::Symbol))
        .push(theme_button)
        .push(setting_button)