sync commit

This commit is contained in:
Joe Bellus 2022-10-13 19:20:41 -04:00
parent b0d45a5eec
commit 235e14dcea
8 changed files with 363 additions and 243 deletions

View File

@ -3,3 +3,5 @@ use druid::Selector;
pub const PROCESS_WORKBOOK: Selector<()> = Selector::new("process-workbook");
pub const PROCESS_BLOCK: Selector<usize> = Selector::new("process-block");
pub const DELETE_BLOCK: Selector<usize> = Selector::new("delete-block");
pub const RENAME_BLOCK: Selector<usize> = Selector::new("rename-block");

View File

@ -4,13 +4,14 @@ use druid::{
Data, Lens,
};
use crate::editor;
use super::{EditorData, Modals};
#[derive(Clone, Data, Lens, Debug)]
pub struct AppData {
#[data(same_fn = "PartialEq::eq")]
pub filename: Option<String>,
pub blocks: Vector<Block>,
pub modals: Modals,
}
impl Default for AppData {
@ -18,6 +19,7 @@ impl Default for AppData {
Self {
filename: None,
blocks: vector![Block::new("Block #1", 0)],
modals: Modals::default(),
}
}
}
@ -44,7 +46,7 @@ impl AppData {
#[derive(Clone, Data, Lens, Default, PartialEq, Debug)]
pub struct Block {
pub name: String,
pub editor_data: editor::EditorData,
pub editor_data: EditorData,
#[data(same_fn = "PartialEq::eq")]
pub output: Output,
pub index: usize,
@ -63,7 +65,7 @@ impl Block {
pub fn new_with_content(name: &str, index: usize, content: &str) -> Self {
Self {
name: name.to_string(),
editor_data: editor::EditorData::new(content),
editor_data: super::EditorData::new(content),
output: Default::default(),
index,
}

View File

@ -0,0 +1,203 @@
use std::ops::Range;
use druid::{Data, Lens};
use ropey::Rope;
#[derive(Clone, Data, PartialEq, Eq, Debug)]
pub enum EditMode {
Normal,
Insert,
}
#[derive(Data, Lens, Clone, PartialEq, Debug)]
pub struct EditorData {
#[data(same_fn = "PartialEq::eq")]
pub content: Rope,
pub cursor_pos: usize,
pub mode: EditMode,
pub cursor_opactiy: f64,
pub cursor_fade: f64,
pub selection_pos: usize,
}
impl Default for EditorData {
fn default() -> Self {
Self {
content: Rope::from_str("let x = 1;\nx+1"),
cursor_pos: 5,
selection_pos: 5,
mode: EditMode::Normal,
cursor_opactiy: 255.0,
cursor_fade: 1.0,
}
}
}
impl EditorData {
pub fn new(content: &str) -> Self {
println!("new block: {}", content);
Self {
content: Rope::from_str(content),
..Default::default()
}
}
pub fn move_cursor(&mut self, idx: usize) {
if idx <= self.content.len_chars() {
self.cursor_pos = idx;
self.deselect();
// dbg!(self.content.char(self.cursor_pos));
}
}
pub fn select_range(&self) -> Range<usize> {
if self.cursor_pos > self.selection_pos {
self.selection_pos..self.cursor_pos
} else {
self.cursor_pos..self.selection_pos
}
}
pub fn push_str(&mut self, s: &str) {
if self.selection_pos != self.cursor_pos {
self.content.remove(self.select_range());
self.content.insert(self.select_range().start, s);
} else {
self.content.insert(self.cursor_pos, s);
}
self.move_cursor(self.select_range().start + s.len());
}
pub fn push(&mut self, c: char) {
self.content.insert_char(self.cursor_pos, c);
self.cursor_right();
}
// pub fn cursor_to_end(&mut self) {
// self.move_cursor(self.content.len_chars());
// }
pub fn deselect(&mut self) {
self.selection_pos = self.cursor_pos;
}
pub fn delete_char_forward(&mut self) {
if !self.select_range().is_empty() {
let range = self.select_range();
self.content.remove(self.select_range());
self.move_cursor(range.start);
return;
}
if self.cursor_pos < self.content.len_chars() {
self.content.remove((self.cursor_pos)..self.cursor_pos + 1);
}
}
pub fn delete_char_back(&mut self) {
// Delete selection
if !self.select_range().is_empty() {
let range = self.select_range();
self.content.remove(self.select_range());
self.move_cursor(range.start);
return;
}
// Cant delete character sif we are at the start of the buffer
if self.cursor_pos > 0 {
self.content.remove((self.cursor_pos - 1)..self.cursor_pos);
self.cursor_left();
}
}
pub fn cursor_left(&mut self) {
if self.cursor_pos > 0 {
self.move_cursor(self.cursor_pos - 1);
}
}
pub fn cursor_up(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
if line_idx > 0 {
let start_of_current_line = self.content.line_to_char(line_idx);
let line_pos = self.cursor_pos - start_of_current_line;
let up_line_start = self.content.line_to_char(line_idx - 1);
let up_line = self.content.line(line_idx - 1);
self.move_cursor(up_line_start + line_pos.min(up_line.len_chars() - 1));
}
}
pub fn cursor_down(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
if line_idx < self.content.len_lines() - 1 {
let start_of_current_line = self.content.line_to_char(line_idx);
let line_pos = self.cursor_pos - start_of_current_line;
let start_of_next_line = self.content.line_to_char(line_idx + 1);
let next_line_len = self.content.line(line_idx + 1).len_chars();
self.move_cursor(start_of_next_line + line_pos.min(next_line_len));
}
}
pub fn cursor_right(&mut self) {
if self.cursor_pos < self.content.len_chars() {
self.move_cursor(self.cursor_pos + 1);
}
}
pub fn select_to_end_of_line(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
let start_of_line = self.content.line_to_char(line_idx);
let line = self.content.line(line_idx);
if line_idx == self.content.len_lines() - 1 {
self.selection_pos = start_of_line + line.len_chars();
} else {
self.selection_pos = start_of_line + line.len_chars() - 1;
}
}
pub fn cursor_to_end_of_line(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
let start_of_line = self.content.line_to_char(line_idx);
let line = self.content.line(line_idx);
if line_idx == self.content.len_lines() - 1 {
self.move_cursor(start_of_line + line.len_chars());
} else {
self.move_cursor(start_of_line + line.len_chars() - 1);
}
}
pub fn select_to_start_of_line(&mut self) {
let start_of_line = self
.content
.line_to_char(self.content.char_to_line(self.cursor_pos));
self.selection_pos = start_of_line;
}
pub fn cursor_to_start_of_line(&mut self) {
let start_of_line = self
.content
.line_to_char(self.content.char_to_line(self.cursor_pos));
self.move_cursor(start_of_line);
}
pub fn select_all(&mut self) {
self.selection_pos = self.content.len_chars();
self.cursor_pos = 0;
}
pub fn select_left(&mut self) {
if self.cursor_pos > 0 {
self.selection_pos -= 1;
}
}
pub fn select_right(&mut self) {
if self.cursor_pos < self.content.len_chars() {
self.selection_pos += 1;
}
}
}

View File

@ -0,0 +1,7 @@
mod app_data;
mod editor_data;
mod modals;
pub use app_data::{AppData, Block};
pub use editor_data::{EditMode, EditorData};
pub use modals::*;

View File

@ -0,0 +1,13 @@
use druid::{Data, Lens};
#[derive(Clone, Data, Lens, Default, Debug)]
pub struct RenameBlock {
pub name: String,
pub input: String,
pub block_index: usize,
}
#[derive(Clone, Data, Lens, Default, Debug)]
pub struct Modals {
pub rename_block: Option<RenameBlock>,
}

View File

@ -1,222 +1,24 @@
use std::ops::Range;
use abacus_core::Output;
use clipboard::ClipboardProvider;
use druid::{
piet::{CairoTextLayout, Text, TextAttribute, TextLayout, TextLayoutBuilder},
widget::{Container, Flex, Label, Padding, Svg, SvgData},
Color, Data, Event, FontDescriptor, FontFamily, FontWeight, Lens, LifeCycle, PaintCtx, Rect,
RenderContext, Target, Widget, WidgetExt,
Color, Event, FontDescriptor, FontFamily, FontWeight, LifeCycle, PaintCtx, Rect, RenderContext,
Target, Widget, WidgetExt,
};
use ropey::Rope;
use syntect::easy::HighlightLines;
use syntect::highlighting::{Style, ThemeSet};
use syntect::parsing::SyntaxSet;
use crate::{app_header::ToolbarButtonController, Block};
use crate::{
app_header::ToolbarButtonController,
data::{EditMode, EditorData},
Block,
};
const FONT_SIZE: f64 = 16.0;
#[derive(Clone, Data, PartialEq, Eq, Debug)]
pub enum EditMode {
Normal,
Insert,
}
#[derive(Data, Lens, Clone, PartialEq, Debug)]
pub struct EditorData {
#[data(same_fn = "PartialEq::eq")]
pub content: Rope,
pub cursor_pos: usize,
pub mode: EditMode,
pub cursor_opactiy: f64,
pub cursor_fade: f64,
pub selection_pos: usize,
}
impl Default for EditorData {
fn default() -> Self {
Self {
content: Rope::from_str("let x = 1;\nx+1"),
cursor_pos: 5,
selection_pos: 5,
mode: EditMode::Normal,
cursor_opactiy: 255.0,
cursor_fade: 1.0,
}
}
}
impl EditorData {
pub fn new(content: &str) -> Self {
println!("new block: {}", content);
Self {
content: Rope::from_str(content),
..Default::default()
}
}
pub fn move_cursor(&mut self, idx: usize) {
if idx <= self.content.len_chars() {
self.cursor_pos = idx;
self.deselect();
// dbg!(self.content.char(self.cursor_pos));
}
}
pub fn select_range(&self) -> Range<usize> {
if self.cursor_pos > self.selection_pos {
self.selection_pos..self.cursor_pos
} else {
self.cursor_pos..self.selection_pos
}
}
pub fn push_str(&mut self, s: &str) {
if self.selection_pos != self.cursor_pos {
self.content.remove(self.select_range());
self.content.insert(self.select_range().start, s);
} else {
self.content.insert(self.cursor_pos, s);
}
self.move_cursor(self.select_range().start + s.len());
}
pub fn push(&mut self, c: char) {
self.content.insert_char(self.cursor_pos, c);
self.cursor_right();
}
// pub fn cursor_to_end(&mut self) {
// self.move_cursor(self.content.len_chars());
// }
pub fn deselect(&mut self) {
self.selection_pos = self.cursor_pos;
}
pub fn delete_char_forward(&mut self) {
if !self.select_range().is_empty() {
let range = self.select_range();
self.content.remove(self.select_range());
self.move_cursor(range.start);
return;
}
if self.cursor_pos < self.content.len_chars() {
self.content.remove((self.cursor_pos)..self.cursor_pos + 1);
}
}
pub fn delete_char_back(&mut self) {
// Delete selection
if !self.select_range().is_empty() {
let range = self.select_range();
self.content.remove(self.select_range());
self.move_cursor(range.start);
return;
}
// Cant delete character sif we are at the start of the buffer
if self.cursor_pos > 0 {
self.content.remove((self.cursor_pos - 1)..self.cursor_pos);
self.cursor_left();
}
}
pub fn cursor_left(&mut self) {
if self.cursor_pos > 0 {
self.move_cursor(self.cursor_pos - 1);
}
}
pub fn cursor_up(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
if line_idx > 0 {
let start_of_current_line = self.content.line_to_char(line_idx);
let line_pos = self.cursor_pos - start_of_current_line;
let up_line_start = self.content.line_to_char(line_idx - 1);
let up_line = self.content.line(line_idx - 1);
self.move_cursor(up_line_start + line_pos.min(up_line.len_chars() - 1));
}
}
pub fn cursor_down(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
if line_idx < self.content.len_lines() - 1 {
let start_of_current_line = self.content.line_to_char(line_idx);
let line_pos = self.cursor_pos - start_of_current_line;
let start_of_next_line = self.content.line_to_char(line_idx + 1);
let next_line_len = self.content.line(line_idx + 1).len_chars();
self.move_cursor(start_of_next_line + line_pos.min(next_line_len));
}
}
pub fn cursor_right(&mut self) {
if self.cursor_pos < self.content.len_chars() {
self.move_cursor(self.cursor_pos + 1);
}
}
pub fn select_to_end_of_line(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
let start_of_line = self.content.line_to_char(line_idx);
let line = self.content.line(line_idx);
if line_idx == self.content.len_lines() - 1 {
self.selection_pos = start_of_line + line.len_chars();
} else {
self.selection_pos = start_of_line + line.len_chars() - 1;
}
}
pub fn cursor_to_end_of_line(&mut self) {
let line_idx = self.content.char_to_line(self.cursor_pos);
let start_of_line = self.content.line_to_char(line_idx);
let line = self.content.line(line_idx);
if line_idx == self.content.len_lines() - 1 {
self.move_cursor(start_of_line + line.len_chars());
} else {
self.move_cursor(start_of_line + line.len_chars() - 1);
}
}
pub fn select_to_start_of_line(&mut self) {
let start_of_line = self
.content
.line_to_char(self.content.char_to_line(self.cursor_pos));
self.selection_pos = start_of_line;
}
pub fn cursor_to_start_of_line(&mut self) {
let start_of_line = self
.content
.line_to_char(self.content.char_to_line(self.cursor_pos));
self.move_cursor(start_of_line);
}
pub fn select_all(&mut self) {
self.selection_pos = self.content.len_chars();
self.cursor_pos = 0;
}
pub fn select_left(&mut self) {
if self.cursor_pos > 0 {
self.selection_pos -= 1;
}
}
pub fn select_right(&mut self) {
if self.cursor_pos < self.content.len_chars() {
self.selection_pos += 1;
}
}
}
pub struct AbacusEditor {
syntax_set: SyntaxSet,
theme_set: ThemeSet,
@ -224,12 +26,9 @@ pub struct AbacusEditor {
impl Default for AbacusEditor {
fn default() -> Self {
let syntax_set = SyntaxSet::load_defaults_newlines();
let theme_set = ThemeSet::load_defaults();
Self {
syntax_set,
theme_set,
syntax_set: SyntaxSet::load_defaults_newlines(),
theme_set: ThemeSet::load_defaults(),
}
}
}
@ -331,16 +130,6 @@ impl AbacusEditor {
impl Widget<EditorData> for AbacusEditor {
fn paint(&mut self, ctx: &mut druid::PaintCtx, data: &EditorData, _env: &druid::Env) {
println!("test");
// let size = ctx.size();
// let rect = size.to_rect();
// if ctx.is_focused() {
// ctx.fill(rect, &Color::rgb8(30, 30, 30));
// } else {
// ctx.fill(rect, &Color::rgb8(20, 20, 20));
// }
if data.content.len_chars() == 0 {
return;
}
@ -628,7 +417,10 @@ pub fn editor_header() -> impl Widget<Block> {
.with_weight(FontWeight::BOLD)
.with_size(14.0),
)
.padding(5.0),
.padding(5.0)
.on_click(|ctx, data, _| {
ctx.submit_command(crate::commands::RENAME_BLOCK.with(data.index));
}),
)
.with_flex_spacer(1.0)
.with_child(

View File

@ -2,33 +2,38 @@ mod app_delegate;
mod app_header;
mod commands;
mod data;
mod modal_container;
mod editor;
mod output_block;
use data::{AppData, Block};
use druid::widget::{Container, Flex, List, Padding, Scroll};
use druid::{AppLauncher, PlatformError, Widget, WidgetExt, WindowDesc};
use modal_container::ModalContainer;
fn build_ui() -> impl Widget<AppData> {
Flex::column()
.with_child(app_header::app_header_ui())
.with_child(app_header::header_separater())
.with_flex_child(
Scroll::new(
List::new(|| {
Flex::column()
.with_child(editor::editor_header())
.with_child(Container::new(Padding::new(
10.0,
editor::AbacusEditor::default().lens(Block::editor_data),
)))
.with_child(output_block::output_block())
})
.lens(AppData::blocks),
)
.vertical(),
1.0,
)
ModalContainer::new(
Flex::column()
.with_child(app_header::app_header_ui())
.with_child(app_header::header_separater())
.with_flex_child(
Scroll::new(
List::new(|| {
Flex::column()
.with_child(editor::editor_header())
.with_child(Container::new(Padding::new(
10.0,
editor::AbacusEditor::default().lens(Block::editor_data),
)))
.with_child(output_block::output_block())
})
.lens(AppData::blocks),
)
.vertical(),
1.0,
),
)
}
fn main() -> Result<(), PlatformError> {

View File

@ -0,0 +1,96 @@
use crate::data;
use druid::{
widget::{Container, Label},
RenderContext, Widget, WidgetPod,
};
pub struct ModalContainer {
child: WidgetPod<data::AppData, Box<dyn Widget<data::AppData>>>,
modal: Option<WidgetPod<data::AppData, Box<dyn Widget<data::AppData>>>>,
}
impl ModalContainer {
pub fn new(child: impl Widget<data::AppData> + 'static) -> Self {
Self {
child: WidgetPod::new(child).boxed(),
modal: None,
}
}
}
impl Widget<data::AppData> for ModalContainer {
fn event(
&mut self,
ctx: &mut druid::EventCtx,
event: &druid::Event,
data: &mut data::AppData,
env: &druid::Env,
) {
if let druid::Event::Command(c) = event {
if let Some(idx) = c.get(crate::commands::RENAME_BLOCK) {
println!("rename block");
data.modals.rename_block = Some(data::RenameBlock {
block_index: *idx,
name: data
.blocks
.get(*idx)
.map(|i| i.name.clone())
.unwrap_or_default(),
input: String::new(),
});
self.modal = Some(WidgetPod::new(rename_block()).boxed());
ctx.children_changed();
return;
}
}
if self.modal.is_none() {
self.child.event(ctx, event, data, env);
}
}
fn lifecycle(
&mut self,
ctx: &mut druid::LifeCycleCtx,
event: &druid::LifeCycle,
data: &data::AppData,
env: &druid::Env,
) {
self.child.lifecycle(ctx, event, data, env);
}
fn update(
&mut self,
ctx: &mut druid::UpdateCtx,
_old_data: &data::AppData,
data: &data::AppData,
env: &druid::Env,
) {
self.child.update(ctx, data, env);
}
fn layout(
&mut self,
ctx: &mut druid::LayoutCtx,
bc: &druid::BoxConstraints,
data: &data::AppData,
env: &druid::Env,
) -> druid::Size {
self.child.layout(ctx, bc, data, env)
}
fn paint(&mut self, ctx: &mut druid::PaintCtx, data: &data::AppData, env: &druid::Env) {
println!("paint modal container");
self.child.paint(ctx, data, env);
let full_rect = ctx.size().to_rect();
if let Some(m) = self.modal.as_mut() {
println!("painting modal");
ctx.fill(full_rect, &druid::Color::rgba8(0, 0, 0, 100));
m.paint(ctx, data, env)
}
}
}
fn rename_block() -> impl Widget<data::AppData> {
Container::new(Label::new("rename block").with_text_color(druid::Color::WHITE))
}