palette imporovement

This commit is contained in:
Dongdong Zhou 2020-12-07 18:32:13 +00:00
parent a27ccc5a84
commit 15dd3a42e8
4 changed files with 276 additions and 168 deletions

120
Cargo.lock generated
View File

@ -150,6 +150,12 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bincode"
version = "1.3.1"
@ -241,6 +247,12 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0017894339f586ccb943b01b9555de56770c11cda818e7e3d8bd93f4ed7f46e"
[[package]]
name = "bytemuck"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41aa2ec95ca3b5c54cf73c91acf06d24f4495d5f1b1c12506ae3483d646177ac"
[[package]]
name = "byteorder"
version = "1.3.4"
@ -712,7 +724,7 @@ dependencies = [
"simple_logger",
"unic-langid",
"unicode-segmentation",
"usvg",
"usvg 0.10.0",
"xi-unicode 0.2.1",
]
@ -870,6 +882,18 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fontdb"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccfec742e7db963305c1d8fde9edef949b47b01e1664dae127489928633fbea8"
dependencies = [
"log",
"memmap2",
"ttf-parser 0.9.0",
"uuid 0.8.1",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -1623,7 +1647,7 @@ dependencies = [
"toml",
"tree-sitter",
"tree-sitter-highlight",
"uuid",
"uuid 0.7.4",
"xi-core-lib",
"xi-rope",
"xi-rpc",
@ -1655,7 +1679,8 @@ dependencies = [
"toml",
"tree-sitter",
"tree-sitter-highlight",
"uuid",
"usvg 0.12.0",
"uuid 0.7.4",
"xi-core-lib",
"xi-rope",
"xi-rpc",
@ -2802,6 +2827,15 @@ dependencies = [
"xmlparser",
]
[[package]]
name = "roxmltree"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17dfc6c39f846bfc7d2ec442ad12055d79608d501380789b965d22f9354451f2"
dependencies = [
"xmlparser",
]
[[package]]
name = "rustc-demangle"
version = "0.1.17"
@ -2823,6 +2857,22 @@ dependencies = [
"semver",
]
[[package]]
name = "rustybuzz"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10"
dependencies = [
"bitflags",
"bytemuck",
"smallvec 1.4.2",
"ttf-parser 0.9.0",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-general-category",
"unicode-script",
]
[[package]]
name = "ryu"
version = "1.0.5"
@ -3396,6 +3446,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
[[package]]
name = "ttf-parser"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca"
[[package]]
name = "type-map"
version = "0.3.0"
@ -3489,6 +3545,24 @@ dependencies = [
"matches",
]
[[package]]
name = "unicode-bidi-mirroring"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
[[package]]
name = "unicode-ccc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbf4a1771ba99c4c8f6e5b1a37a208e970e77ed7297e29963dd1a2303601cd33"
[[package]]
name = "unicode-general-category"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f"
[[package]]
name = "unicode-normalization"
version = "0.1.13"
@ -3582,11 +3656,38 @@ dependencies = [
"memmap2",
"pico-args",
"rctree",
"roxmltree",
"roxmltree 0.11.0",
"simplecss",
"siphasher",
"svgtypes",
"ttf-parser",
"ttf-parser 0.6.2",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
]
[[package]]
name = "usvg"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05a7ff57fca79848360bd2c00072a8029543cf554fe49c4d0af247271434702e"
dependencies = [
"base64 0.13.0",
"data-url",
"flate2",
"fontdb",
"kurbo 0.7.0",
"log",
"memmap2",
"pico-args",
"rctree",
"roxmltree 0.13.0",
"rustybuzz",
"simplecss",
"siphasher",
"svgtypes",
"ttf-parser 0.9.0",
"unicode-bidi",
"unicode-script",
"unicode-vo",
@ -3608,6 +3709,15 @@ dependencies = [
"rand 0.6.5",
]
[[package]]
name = "uuid"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
dependencies = [
"rand 0.7.3",
]
[[package]]
name = "vcpkg"
version = "0.2.10"

View File

@ -5,6 +5,7 @@ authors = ["Dongdong Zhou <dzhou121@gmail.com>"]
edition = "2018"
[dependencies]
usvg = "0.12.0"
lapce-proxy = { path = "../proxy" }
xi-trace = { path = "../../xi-editor/rust/trace/" }
git2 = "0.13"

View File

@ -62,10 +62,10 @@ pub fn new(window_id: WindowId, tab_id: WidgetId) -> Self {
let palette = state.palette.lock();
(palette.widget_id.clone(), palette.scroll_widget_id.clone())
};
let palette = Palette::new(window_id, tab_id, scroll_widget_id)
.with_id(widget_id)
.border(theme::BORDER_LIGHT, 1.0)
.background(LapceTheme::EDITOR_SELECTION_COLOR);
let palette =
Palette::new(window_id, tab_id, scroll_widget_id).with_id(widget_id);
// .border(theme::BORDER_LIGHT, 1.0)
// .background(LapceTheme::EDITOR_SELECTION_COLOR)
let palette = WidgetPod::new(palette).boxed();
let editor_split_state = state.editor_split.lock();
@ -290,8 +290,8 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) {
.lock()
== LapceFocus::Palette
{
let blur_color = Color::grey8(100);
ctx.blurred_rect(self.palette.layout_rect(), 5.0, &blur_color);
// let blur_color = Color::grey8(100);
// ctx.blurred_rect(self.palette.layout_rect(), 5.0, &blur_color);
self.palette.paint(ctx, data, env);
}

View File

@ -4,11 +4,12 @@
kurbo::{Line, Rect},
piet::TextAttribute,
widget::Container,
widget::FillStrat,
widget::IdentityWrapper,
widget::Svg,
widget::SvgData,
Affine, Command, ExtEventSink, FontFamily, FontWeight, KeyEvent, Target, Vec2,
WidgetId, WindowId,
Affine, Command, ExtEventSink, FontFamily, FontWeight, Insets, KeyEvent, Target,
Vec2, WidgetId, WindowId,
};
use druid::{
piet::{Text, TextLayout as PietTextLayout, TextLayoutBuilder},
@ -33,6 +34,7 @@
sync::mpsc::channel,
};
use std::{marker::PhantomData, sync::mpsc::Sender};
use usvg;
use uuid::Uuid;
use crate::{
@ -82,9 +84,11 @@ pub struct PaletteState {
tab_id: WidgetId,
pub widget_id: WidgetId,
pub scroll_widget_id: WidgetId,
scroll_offset: f64,
input: String,
cursor: usize,
items: Vec<PaletteItem>,
filtered_items: Vec<PaletteItem>,
index: usize,
palette_type: PaletteType,
run_id: String,
@ -102,7 +106,9 @@ pub fn new(window_id: WindowId, tab_id: WidgetId) -> PaletteState {
widget_id,
scroll_widget_id: WidgetId::next(),
items: Vec::new(),
filtered_items: Vec::new(),
input: "".to_string(),
scroll_offset: 0.0,
cursor: 0,
index: 0,
rev: 0,
@ -234,18 +240,13 @@ fn update_palette(&mut self) {
&PaletteType::Command => self.items = self.get_commands(),
}
LAPCE_APP_STATE
.submit_ui_command(LapceUICommand::RequestLayout, self.widget_id);
.submit_ui_command(LapceUICommand::RequestPaint, self.widget_id);
return;
// self.request_layout(ctx);
} else {
self.sender.send(self.rev);
// self.filter_items();
// self.request_layout(ctx);
// self.preview(ctx, ui_state, env);
}
LAPCE_APP_STATE
.submit_ui_command(LapceUICommand::RequestLayout, self.widget_id);
// self.ensure_visible(ctx, env);
.submit_ui_command(LapceUICommand::RequestPaint, self.widget_id);
}
pub fn move_cursor(&mut self, ctx: &mut EventCtx, n: i64) {
@ -312,27 +313,6 @@ pub fn get_input(&self) -> &str {
}
}
pub fn filter_items(&mut self) {
let input = self.get_input().to_string();
for item in self.items.iter_mut() {
if input == "" {
item.score = -1.0 - item.index as f64;
item.match_mask = BitVec::new();
} else {
let text = item.get_text();
if has_match(&input, &text) {
let result = locate(&input, &text);
item.score = result.score;
item.match_mask = result.match_mask;
} else {
item.score = f64::NEG_INFINITY;
}
}
}
self.items
.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(Ordering::Less));
}
fn get_commands(&self) -> Vec<PaletteItem> {
let commands = vec![("Open Folder", LapceCommand::OpenFolder)];
commands
@ -491,7 +471,7 @@ fn get_document_symbols(&self) -> Option<()> {
palette.update_palette();
}
LAPCE_APP_STATE.submit_ui_command(
LapceUICommand::RequestLayout,
LapceUICommand::RequestPaint,
widget_id,
);
}
@ -659,7 +639,7 @@ fn get_files(&self) {
palette.update_palette();
} else {
LAPCE_APP_STATE.submit_ui_command(
LapceUICommand::RequestLayout,
LapceUICommand::RequestPaint,
widget_id,
);
}
@ -814,11 +794,12 @@ fn get_local_files(&self) -> Vec<PaletteItem> {
items
}
pub fn current_items(&self) -> Vec<&PaletteItem> {
self.items
.iter()
.filter(|i| i.score != f64::NEG_INFINITY)
.collect()
pub fn current_items(&self) -> &Vec<PaletteItem> {
if self.get_input() == "" {
&self.items
} else {
&self.filtered_items
}
}
pub fn get_item(&self) -> Option<&PaletteItem> {
@ -921,30 +902,14 @@ pub fn start_filter_process(
(palette.get_input().to_string(), palette.items.clone())
};
for item in items.iter_mut() {
if input == "" {
item.score = -1.0 - item.index as f64;
item.match_mask = BitVec::new();
} else {
let text = item.get_text();
if has_match(&input, &text) {
let result = locate(&input, &text);
item.score = result.score;
item.match_mask = result.match_mask;
} else {
item.score = f64::NEG_INFINITY;
}
}
}
items
.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(Ordering::Less));
let items = filter_items(&input, items);
let state = LAPCE_APP_STATE.get_tab_state(&window_id, &tab_id);
let mut palette = state.palette.lock();
if palette.rev != rev {
continue;
}
palette.items = items;
palette.filtered_items = items;
LAPCE_APP_STATE.submit_ui_command(LapceUICommand::FilterItems, widget_id);
}
}
@ -973,15 +938,14 @@ pub fn new(
tab_id: WidgetId,
scroll_id: WidgetId,
) -> Palette {
let padding = 6.0;
let palette_input = PaletteInput::new(window_id, tab_id)
.padding((5.0, 5.0, 5.0, 5.0))
.padding((padding, padding, padding, padding * 2.0))
.background(LapceTheme::EDITOR_BACKGROUND)
.padding((5.0, 5.0, 5.0, 5.0));
let palette_content =
LapceScroll::new(PaletteContent::new(window_id, tab_id))
.vertical()
.with_id(scroll_id)
.padding((5.0, 0.0, 5.0, 0.0));
.padding((10.0 + padding, 10.0 + padding, 10.0 + padding, padding));
let palette_content = PaletteContent::new(window_id, tab_id)
.with_id(scroll_id)
.padding((10.0 + padding, 0.0, 10.0 + padding, 10.0 + padding));
let palette = Palette {
window_id,
tab_id,
@ -1040,7 +1004,7 @@ fn event(
.get_tab_state(&self.window_id, &self.tab_id);
let mut palette = state.palette.lock();
palette.preview(ctx, data, env);
ctx.request_layout();
ctx.request_paint();
}
_ => (),
}
@ -1069,22 +1033,6 @@ fn update(
data: &LapceUIState,
env: &Env,
) {
// if data.palette.same(&old_data.palette) {
// return;
// }
// if data.focus == LapceFocus::Palette {
// if old_data.focus == LapceFocus::Palette {
// self.input.update(ctx, data, env);
// self.content.update(ctx, data, env);
// } else {
// ctx.request_layout();
// }
// } else {
// if old_data.focus == LapceFocus::Palette {
// ctx.request_paint();
// }
// }
}
fn layout(
@ -1112,21 +1060,47 @@ fn layout(
.with_size(content_size),
);
// flex_size
let size =
Size::new(bc.max().width, content_size.height + input_size.height);
let size = Size::new(
bc.max().width,
content_size.height + 10.0 + 6.0 * 2.0 + input_size.height,
);
size
}
fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) {
if *LAPCE_APP_STATE
.get_tab_state(&self.window_id, &self.tab_id)
.focus
.lock()
!= LapceFocus::Palette
let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id);
{
return;
if *state.focus.lock() != LapceFocus::Palette {
return;
}
}
let rects = ctx.region().rects();
let shadow_width = 5.0;
let shift = shadow_width * 2.0;
let line_height = env.get(LapceTheme::EDITOR_LINE_HEIGHT);
let items_len = {
let palette = state.palette.lock();
palette.current_items().len()
};
let max_items = 16;
let height = if items_len > max_items {
max_items
} else {
items_len
};
let height = if height > 0 {
line_height * height as f64 + 6.0
} else {
0.0
};
let height = height + 13.0 + 6.0 * 5.0;
let size = Size::new(ctx.size().width, height + shift * 2.0);
let content_rect = size.to_rect() - Insets::new(shift, shift, shift, shift);
let blur_color = Color::grey8(100);
ctx.blurred_rect(content_rect, shadow_width, &blur_color);
ctx.fill(content_rect, &env.get(LapceTheme::EDITOR_SELECTION_COLOR));
self.input.paint(ctx, data, env);
self.content.paint(ctx, data, env);
}
@ -1158,17 +1132,6 @@ fn update(
data: &LapceUIState,
env: &Env,
) {
// if data.palette.index != old_data.palette.index {
// ctx.request_paint()
// }
// if data.palette.filtered_items.len()
// != old_data.palette.filtered_items.len()
// {
// ctx.request_layout()
// }
// if data.palette.items.len() != old_data.palette.items.len() {
// ctx.request_layout()
// }
}
fn layout(
@ -1179,10 +1142,8 @@ fn layout(
env: &Env,
) -> Size {
let line_height = env.get(LapceTheme::EDITOR_LINE_HEIGHT);
let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id);
let palette = state.palette.lock();
let items_len = palette.current_items().len();
let height = { line_height * items_len as f64 };
let max_items = 15;
let height = line_height * max_items as f64;
Size::new(bc.max().width, height)
}
@ -1190,53 +1151,61 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) {
let line_height = env.get(LapceTheme::EDITOR_LINE_HEIGHT);
let rects = ctx.region().rects().to_vec();
let state = LAPCE_APP_STATE.get_tab_state(&self.window_id, &self.tab_id);
let palette = state.palette.lock();
let mut palette = state.palette.lock();
let height = line_height * 15.0;
for rect in rects {
let start = (rect.y0 / line_height).floor() as usize;
let items = {
let items = palette.current_items();
let items_len = items.len();
&items[start
..((rect.y1 / line_height).floor() as usize + 1).min(items_len)]
.to_vec()
let items = palette.current_items();
let items_height = items.len() as f64 * line_height;
let current_line_offset = palette.index as f64 * line_height;
let scroll_offset = if palette.scroll_offset
< current_line_offset + line_height - height
{
(current_line_offset + line_height - height)
.min(items_height - height)
} else if palette.scroll_offset > current_line_offset {
current_line_offset
} else {
palette.scroll_offset
};
for (i, item) in items.iter().enumerate() {
if palette.index == start + i {
let start = (scroll_offset / line_height).floor() as usize;
let num_lines = 15;
for line in start..start + num_lines + 1 {
if line >= items.len() {
break;
}
let item = &items[line];
if palette.index == line {
if let Some(background) = LAPCE_APP_STATE.theme.get("background")
{
ctx.fill(
Rect::ZERO
.with_origin(Point::new(
rect.x0,
(start + i) as f64 * line_height,
line as f64 * line_height - scroll_offset,
))
.with_size(Size::new(rect.width(), line_height)),
background,
)
}
}
match &item.icon {
PaletteIcon::File(exten) => {
if let Some(svg_data) = file_svg(&exten) {
let x = 1.0;
let y = (start + i) as f64 * line_height + 2.0;
let affine = Affine::new([0.5, 0.0, 0.0, 0.5, x, y]);
svg_data.to_piet(affine, ctx);
}
}
PaletteIcon::Symbol(symbol) => {
if let Some(svg) = symbol_svg(&symbol) {
svg.to_piet(
Affine::translate(Vec2::new(
1.0,
(start + i) as f64 * line_height + 2.0,
)),
ctx,
);
}
}
_ => (),
if let Some((svg_data, svg_tree)) = match &item.icon {
PaletteIcon::File(exten) => file_svg(&exten),
PaletteIcon::Symbol(symbol) => symbol_svg(&symbol),
_ => None,
} {
let svg_size = svg_tree_size(&svg_tree);
let scale = 13.0 / svg_size.height;
let affine = Affine::new([
scale,
0.0,
0.0,
scale,
1.0,
line as f64 * line_height + 5.0 - scroll_offset,
]);
svg_data.to_piet(affine, ctx);
}
let mut text_layout = ctx
.text()
@ -1276,7 +1245,10 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) {
let text_layout = text_layout.build().unwrap();
ctx.draw_text(
&text_layout,
Point::new(20.0, (start + i) as f64 * line_height),
Point::new(
20.0,
line as f64 * line_height + 5.0 - scroll_offset,
),
);
let text_x =
@ -1307,27 +1279,33 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) {
&text_layout,
Point::new(
20.0 + text_x + 5.0,
(start + i) as f64 * line_height + 1.0,
line as f64 * line_height + 6.0 - scroll_offset,
),
);
}
}
palette.scroll_offset = scroll_offset;
}
}
}
fn file_svg(exten: &str) -> Option<SvgData> {
Some(
SvgData::from_str(
ICONS_DIR
.get_file(format!("file_type_{}.svg", exten))?
.contents_utf8()?,
)
.ok()?,
)
fn get_svg(name: &str) -> Option<(SvgData, usvg::Tree)> {
let content = ICONS_DIR.get_file(name)?.contents_utf8()?;
let opt = usvg::Options {
keep_named_groups: false,
..usvg::Options::default()
};
let usvg_tree = usvg::Tree::from_str(&content, &opt).ok()?;
Some((SvgData::from_str(&content).ok()?, usvg_tree))
}
fn symbol_svg(kind: &SymbolKind) -> Option<SvgData> {
fn file_svg(exten: &str) -> Option<(SvgData, usvg::Tree)> {
get_svg(&format!("file_type_{}.svg", exten))
}
fn symbol_svg(kind: &SymbolKind) -> Option<(SvgData, usvg::Tree)> {
let kind_str = match kind {
SymbolKind::Array => "array",
SymbolKind::Boolean => "boolean",
@ -1354,15 +1332,7 @@ fn symbol_svg(kind: &SymbolKind) -> Option<SvgData> {
_ => return None,
};
Some(
SvgData::from_str(
ICONS_DIR
.get_file(format!("symbol-{}.svg", kind_str))
.unwrap()
.contents_utf8()?,
)
.ok()?,
)
get_svg(&format!("symbol-{}.svg", kind_str))
}
impl Widget<LapceUIState> for PaletteInput {
@ -1405,7 +1375,7 @@ fn layout(
data: &LapceUIState,
env: &Env,
) -> Size {
Size::new(bc.max().width, env.get(LapceTheme::EDITOR_LINE_HEIGHT))
Size::new(bc.max().width, 13.0)
}
fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceUIState, env: &Env) {
@ -1498,3 +1468,30 @@ pub fn select(
}
}
}
fn filter_items(input: &str, items: Vec<PaletteItem>) -> Vec<PaletteItem> {
let mut items: Vec<PaletteItem> = items
.iter()
.filter_map(|i| {
let text = i.get_text();
if has_match(&input, &text) {
let result = locate(&input, &text);
let mut item = i.clone();
item.score = result.score;
item.match_mask = result.match_mask;
Some(item)
} else {
None
}
})
.collect();
items.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap_or(Ordering::Less));
items
}
fn svg_tree_size(svg_tree: &usvg::Tree) -> Size {
match *svg_tree.root().borrow() {
usvg::NodeKind::Svg(svg) => Size::new(svg.size.width(), svg.size.height()),
_ => Size::ZERO,
}
}