rewrite to support multi line selection(unfinished)

This commit is contained in:
Anton-4 2020-12-25 19:46:31 +01:00
parent fc310cf974
commit 7a70c95bb4
8 changed files with 169 additions and 30 deletions

1
Cargo.lock generated
View File

@ -2649,6 +2649,7 @@ dependencies = [
"indoc",
"inkwell",
"inlinable_string",
"itertools",
"libc",
"log",
"maplit",

View File

@ -62,6 +62,7 @@ env_logger = "0.7"
futures = "0.3"
wgpu_glyph = "0.10"
cgmath = "0.17.0"
itertools = "0.9.0"
[dependencies.bytemuck]
version = "1.4"

View File

@ -73,8 +73,12 @@ These are potentially inspirational resources for the editor's design.
* Voice input:
* Good for accessibility.
* https://www.youtube.com/watch?v=Ffa3cXM7bjc is interesting for inspiration.
* Describe actions instead of using complicated shortcuts.
* Could be efficient way to communicate with smart assistant.
* Describe actions to execute them, examples:
* Add latest datetime package to dependencies.
* Generate unit test for this function.
* Show edit history for this function.
## General Thoughts/Ideas

2
editor/src/colors.rs Normal file
View File

@ -0,0 +1,2 @@
pub const WHITE: [f32; 3] = [1.0, 1.0, 1.0];

12
editor/src/error.rs Normal file
View File

@ -0,0 +1,12 @@
use std::{error::Error, fmt};
#[derive(Debug)]
pub struct OutOfBounds;
impl Error for OutOfBounds {}
impl fmt::Display for OutOfBounds {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TODO proper error")
}
}

View File

@ -19,6 +19,8 @@ use std::path::Path;
use winit::event;
use winit::event::{Event, ModifiersState};
use winit::event_loop::ControlFlow;
use error::{OutOfBounds};
use vec_result::{get_res};
pub mod ast;
mod buffer;
@ -34,6 +36,9 @@ pub mod text;
mod types;
mod util;
mod vertex;
mod colors;
pub mod error;
mod vec_result;
/// The editor is actually launched from the CLI if you pass it zero arguments,
/// or if you provide it 1 or more files or directories to open on launch.
@ -107,7 +112,7 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
let mut glyph_brush = build_glyph_brush(&gpu_device, render_format)?;
let is_animating = true;
let mut text_state = "A".to_owned();
let mut text_state = "".to_owned();//String::new();
let mut keyboard_modifiers = ModifiersState::empty();
// Render loop
@ -201,29 +206,35 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
&mut glyph_brush,
);
if !glyph_bounds_rects.is_empty() {
let rect_buffers = create_rect_buffers(
&gpu_device,
&mut encoder,
&glyph_bounds_rects,
);
let selection_rects_res = create_selection_rects(1, 10, 2, 10, &glyph_bounds_rects);
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
ops: wgpu::Operations::default(),
}],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&rect_pipeline);
render_pass.set_bind_group(0, &ortho.bind_group, &[]);
render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..));
render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
drop(render_pass);
match selection_rects_res {
Ok(selection_rects) =>
if !selection_rects.is_empty() {
let rect_buffers = create_rect_buffers(
&gpu_device,
&mut encoder,
&selection_rects,
);
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
ops: wgpu::Operations::default(),
}],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&rect_pipeline);
render_pass.set_bind_group(0, &ortho.bind_group, &[]);
render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..));
render_pass.set_index_buffer(rect_buffers.index_buffer.slice(..));
render_pass.draw_indexed(0..rect_buffers.num_rects, 0, 0..1);
drop(render_pass);
},
Err(e) => println!("{:?}", e) //TODO draw error text on screen
}
// draw all text
@ -257,6 +268,83 @@ fn run_event_loop() -> Result<(), Box<dyn Error>> {
})
}
fn create_selection_rects(
start_line: usize,
pos_in_start_line: usize,
stop_line: usize,
pos_in_stop_line: usize,
glyph_bound_rects: &Vec<Vec<Rect>>
) -> Result<Vec<Rect>, OutOfBounds> {
//TODO assert start_line <= stop_line, if start_line == stop_line => pos_in_start_line <= pos_in_stop_line
let mut all_rects = Vec::new();
if start_line == stop_line {
let start_glyph_rect =
get_res(
pos_in_start_line,
get_res(start_line, glyph_bound_rects)?
)?;
let stop_glyph_rect =
get_res(
pos_in_stop_line,
get_res(stop_line, glyph_bound_rects)?
)?;
let top_left_coords =
start_glyph_rect.top_left_coords;
let height = start_glyph_rect.height;
let width = (stop_glyph_rect.top_left_coords.x - start_glyph_rect.top_left_coords.x) + stop_glyph_rect.width;
all_rects.push(
Rect {
top_left_coords,
width,
height,
color: colors::WHITE
}
);
Ok(all_rects)
} else {
let start_line = get_res(start_line, glyph_bound_rects)?;
let start_glyph_rect =
get_res(
pos_in_start_line,
start_line
)?;
let stop_glyph_rect =
get_res(
start_line.len() - 1,
start_line
)?;
let top_left_coords =
start_glyph_rect.top_left_coords;
let height = start_glyph_rect.height;
let width = (stop_glyph_rect.top_left_coords.x - start_glyph_rect.top_left_coords.x) + stop_glyph_rect.width;
all_rects.push(
Rect {
top_left_coords,
width,
height,
color: colors::WHITE
}
);
//TODO loop rects if necessary and stop line rect
Ok(all_rects)
}
}
fn make_rect_pipeline(
gpu_device: &wgpu::Device,
swap_chain_descr: &wgpu::SwapChainDescriptor,
@ -321,11 +409,12 @@ fn create_render_pipeline(
})
}
// returns bounding boxes for every glyph
fn queue_all_text(
size: &winit::dpi::PhysicalSize<u32>,
text_state: &str,
glyph_brush: &mut wgpu_glyph::GlyphBrush<()>,
) -> Vec<Rect> {
) -> Vec<Vec<Rect>> {
let area_bounds = (size.width as f32, size.height as f32).into();
let main_label = Text {

View File

@ -1,10 +1,11 @@
// Adapted from https://github.com/sotrh/learn-wgpu
// by Benjamin Hansen, licensed under the MIT license
use ab_glyph::{FontArc, InvalidFont};
use ab_glyph::{FontArc, InvalidFont, Glyph};
use cgmath::{Vector2, Vector4};
use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, GlyphCruncher, Section};
use crate::rect::Rect;
use itertools::Itertools;
#[derive(Debug)]
pub struct Text {
@ -31,7 +32,8 @@ impl Default for Text {
}
}
pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) -> Vec<Rect> {
// returns bounding boxes for every glyph
pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) -> Vec<Vec<Rect>> {
let layout = wgpu_glyph::Layout::default().h_align(if text.centered {
wgpu_glyph::HorizontalAlign::Center
} else {
@ -57,17 +59,31 @@ pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) -> Vec<Rec
{
let position = section_glyph.glyph.position;
let px_scale = section_glyph.glyph.scale;
let width = px_scale.x * 0.5;
let width = glyph_width(&section_glyph.glyph);
let height = px_scale.y;
let top_y = glyph_top_y(&section_glyph.glyph);
Rect {
top_left_coords: [position.x, position.y - height * 0.75].into(),
top_left_coords: [position.x, top_y].into(),
width,
height,
color: [1.0, 1.0, 1.0]
}
}
).collect()
).group_by(|rect| rect.top_left_coords.y)
.into_iter()
.map(|(_y_coord, rect_group)| rect_group.collect())
.collect()
}
pub fn glyph_top_y(glyph: &Glyph) -> f32 {
let height = glyph.scale.y;
glyph.position.y - height * 0.75
}
pub fn glyph_width(glyph: &Glyph) -> f32 {
glyph.scale.x * 0.5
}
pub fn build_glyph_brush(

14
editor/src/vec_result.rs Normal file
View File

@ -0,0 +1,14 @@
use crate::error::{OutOfBounds};
use std::slice::SliceIndex;
// replace vec methods that return Option with Result and proper Error
pub fn get_res<T>(indx: usize, vec: &Vec<T>) -> Result<&<usize as SliceIndex<[T]>>::Output, OutOfBounds> {
match vec.get(indx) {
Some(elt) =>
Ok(elt),
None =>
Err(OutOfBounds {})
}
}