209 lines
7.3 KiB
Rust
209 lines
7.3 KiB
Rust
// Stiletto
|
|
// Copyright (C) 2020 Stiletto Authors
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
use log::{info, warn};
|
|
|
|
use druid::commands;
|
|
use druid::im::vector;
|
|
use druid::widget::prelude::*;
|
|
use druid::widget::{
|
|
Align, Button, CrossAxisAlignment, Flex, List, ListGrowDirection, SizedBox, WidgetExt,
|
|
};
|
|
use druid::{
|
|
AppDelegate, AppLauncher, Color, Command, Data, DelegateCtx, Env, FileDialogOptions, FileSpec,
|
|
Lens, LocalizedString, Target, WindowDesc,
|
|
};
|
|
|
|
use im::Vector;
|
|
|
|
use stiletto::history::VersionedCanvas;
|
|
use stiletto::tool::{CanvasToolCtx, CanvasToolParams};
|
|
use stiletto::widget::{build_simple_tool_widget, CanvasState, CanvasToolIconState, CanvasWidget};
|
|
use stiletto::DocumentSnapshot;
|
|
|
|
pub fn main() {
|
|
let window = WindowDesc::new(build_ui)
|
|
.window_size((1024.0, 1400.0))
|
|
.title(
|
|
LocalizedString::new("custom-widget-demo-window-title").with_placeholder("Stiletto"),
|
|
);
|
|
|
|
let default_pen_params = CanvasToolParams::Pen {
|
|
thickness: 2.0,
|
|
color: Color::rgb(0, 0, 0),
|
|
};
|
|
let canvas_data = StilettoState {
|
|
canvas: CanvasState {
|
|
versioned_canvas: VersionedCanvas::new(vector![]),
|
|
tool_ctx: CanvasToolCtx::new(default_pen_params.clone()),
|
|
},
|
|
tool_icons: vector![
|
|
CanvasToolIconState {
|
|
tool_params: default_pen_params,
|
|
selected: true,
|
|
},
|
|
CanvasToolIconState {
|
|
tool_params: CanvasToolParams::Eraser,
|
|
selected: false,
|
|
}
|
|
],
|
|
current_tool: 0,
|
|
};
|
|
AppLauncher::with_window(window)
|
|
.use_simple_logger()
|
|
.delegate(Delegate)
|
|
.launch(canvas_data)
|
|
.expect("launch failed");
|
|
}
|
|
|
|
#[derive(Clone, Data, Lens)]
|
|
struct StilettoState {
|
|
canvas: CanvasState,
|
|
tool_icons: Vector<CanvasToolIconState>,
|
|
current_tool: usize,
|
|
}
|
|
|
|
fn build_ui() -> impl Widget<StilettoState> {
|
|
let history_buttons = Flex::row()
|
|
.cross_axis_alignment(CrossAxisAlignment::Center)
|
|
.with_child(Button::new("Undo").on_click(
|
|
|_ctx: &mut EventCtx, data: &mut StilettoState, _env: &Env| data.canvas.perform_undo(),
|
|
))
|
|
.with_child(Button::new("Redo").on_click(
|
|
|_ctx: &mut EventCtx, data: &mut StilettoState, _env: &Env| data.canvas.perform_redo(),
|
|
));
|
|
|
|
let stlt = FileSpec::new("Stiletto notebook", &["stlt"]);
|
|
let save_dialog_options = FileDialogOptions::new()
|
|
.allowed_types(vec![stlt])
|
|
.default_type(stlt);
|
|
let open_dialog_options = save_dialog_options.clone();
|
|
|
|
let save_buttons = Flex::row()
|
|
.cross_axis_alignment(CrossAxisAlignment::Center)
|
|
.with_child(Button::new("Open").on_click(move |ctx, _, _| {
|
|
ctx.submit_command(
|
|
Command::new(
|
|
druid::commands::SHOW_OPEN_PANEL,
|
|
open_dialog_options.clone(),
|
|
),
|
|
None,
|
|
)
|
|
}))
|
|
.with_child(Button::new("Save").on_click(move |ctx, _, _| {
|
|
ctx.submit_command(
|
|
Command::new(
|
|
druid::commands::SHOW_SAVE_PANEL,
|
|
save_dialog_options.clone(),
|
|
),
|
|
None,
|
|
)
|
|
}));
|
|
|
|
let tool_buttons = Flex::row()
|
|
.cross_axis_alignment(CrossAxisAlignment::Center)
|
|
.with_flex_child(
|
|
List::new(|| {
|
|
build_simple_tool_widget(30.0, 30.0, 5.0)
|
|
.padding((8.0, 0.0))
|
|
.on_click(|ctx, tool_state, _| {
|
|
tool_state.selected = true;
|
|
ctx.submit_command(stiletto::commands::UPDATE_TOOL, None);
|
|
})
|
|
})
|
|
.grow(ListGrowDirection::Right)
|
|
.lens(StilettoState::tool_icons),
|
|
1.0,
|
|
);
|
|
|
|
let toolbar = Flex::row()
|
|
.cross_axis_alignment(CrossAxisAlignment::Center)
|
|
.with_spacer(30.0)
|
|
.with_flex_child(Align::left(history_buttons), 1.0)
|
|
.with_spacer(10.0)
|
|
.with_flex_child(Align::left(tool_buttons), 2.0)
|
|
.with_spacer(20.0)
|
|
.with_flex_child(Align::right(save_buttons), 1.0)
|
|
.with_spacer(30.0);
|
|
|
|
Flex::column()
|
|
.cross_axis_alignment(CrossAxisAlignment::Center)
|
|
.must_fill_main_axis(true)
|
|
.with_child(SizedBox::new(Align::left(toolbar)).height(50.0))
|
|
.with_flex_child((CanvasWidget {}).lens(StilettoState::canvas), 1.0)
|
|
}
|
|
|
|
struct Delegate;
|
|
|
|
impl AppDelegate<StilettoState> for Delegate {
|
|
fn command(
|
|
&mut self,
|
|
_ctx: &mut DelegateCtx,
|
|
_target: Target,
|
|
cmd: &Command,
|
|
data: &mut StilettoState,
|
|
_env: &Env,
|
|
) -> bool {
|
|
use std::fs::File;
|
|
|
|
if let Some(Some(file_info)) = cmd.get(commands::SAVE_FILE) {
|
|
let res_file = File::create(file_info.path());
|
|
if let Ok(f) = res_file {
|
|
let write_res =
|
|
serde_json::to_writer_pretty(f, &data.canvas.get_document_snapshot());
|
|
if write_res.is_err() {
|
|
warn!("Error while saving: {:?}", write_res.err());
|
|
} else {
|
|
info!("Written to file: {}", file_info.path().display());
|
|
}
|
|
} else {
|
|
warn!("Cannot create file: {:?}", res_file.err());
|
|
}
|
|
return true;
|
|
}
|
|
if let Some(file_info) = cmd.get(commands::OPEN_FILE) {
|
|
if let Ok(f) = File::open(file_info.path()) {
|
|
let res_snapshot: Result<DocumentSnapshot, serde_json::Error> =
|
|
serde_json::from_reader(f);
|
|
if let Ok(document_snapshot) = res_snapshot {
|
|
data.canvas.set_from_snapshot(document_snapshot);
|
|
info!("Loaded file {}", file_info.path().display());
|
|
} else {
|
|
warn!("didn't work: {:?}", res_snapshot.err());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
if cmd.get(stiletto::commands::UPDATE_TOOL).is_some() {
|
|
let old_tool = data.current_tool;
|
|
if let Some(new_tool) = data
|
|
.tool_icons
|
|
.iter()
|
|
.enumerate()
|
|
.position(|(pos, x)| pos != old_tool && x.selected)
|
|
{
|
|
info!("Changing active tool to: tool #{}", new_tool);
|
|
data.tool_icons.get_mut(old_tool).unwrap().selected = false;
|
|
data.current_tool = new_tool;
|
|
data.canvas.set_tool_ctx(CanvasToolCtx::new(
|
|
data.tool_icons.get(new_tool).unwrap().tool_params.clone(),
|
|
));
|
|
}
|
|
}
|
|
false
|
|
}
|
|
}
|