mirror of https://github.com/lapce/lapce.git
feat(search): add case sensitive search ui button (#1441)
This commit is contained in:
parent
c880b15993
commit
ee83113246
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"><path fill="currentColor" d="M8.854 11.702h-1l-.816-2.159H3.772l-.768 2.16H2L4.954 4h.935l2.965 7.702Zm-2.111-2.97L5.534 5.45a3.142 3.142 0 0 1-.118-.515h-.021c-.036.218-.077.39-.124.515L4.073 8.732h2.67Zm7.013 2.97h-.88v-.86h-.022c-.383.66-.947.99-1.692.99c-.548 0-.978-.146-1.29-.436c-.307-.29-.461-.675-.461-1.155c0-1.027.605-1.625 1.815-1.794l1.65-.23c0-.935-.379-1.403-1.134-1.403c-.663 0-1.26.226-1.794.677V6.59c.54-.344 1.164-.516 1.87-.516c1.292 0 1.938.684 1.938 2.052v3.577Zm-.88-2.782l-1.327.183c-.409.057-.717.159-.924.306c-.208.143-.312.399-.312.768c0 .268.095.489.285.66c.193.169.45.253.768.253a1.41 1.41 0 0 0 1.08-.457c.286-.308.43-.696.43-1.165V8.92Z"/></svg>
|
After Width: | Height: | Size: 797 B |
|
@ -213,6 +213,8 @@ pub enum FocusCommand {
|
|||
SearchForward,
|
||||
#[strum(serialize = "search_backward")]
|
||||
SearchBackward,
|
||||
#[strum(serialize = "toggle_case_sensitive_search")]
|
||||
ToggleCaseSensitive,
|
||||
#[strum(serialize = "global_search_refresh")]
|
||||
GlobalSearchRefresh,
|
||||
#[strum(serialize = "clear_search")]
|
||||
|
|
|
@ -514,6 +514,10 @@ pub enum LapceUICommand {
|
|||
},
|
||||
UpdateSearchInput(String),
|
||||
UpdateSearch(String),
|
||||
UpdateSearchWithCaseSensitivity {
|
||||
pattern: String,
|
||||
case_sensitive: bool,
|
||||
},
|
||||
GlobalSearchResult(String, Arc<HashMap<PathBuf, Vec<Match>>>),
|
||||
CancelFilePicker,
|
||||
SetWorkspace(LapceWorkspace),
|
||||
|
|
|
@ -1308,17 +1308,16 @@ pub fn do_multi_selection(
|
|||
(first.min(), first.max())
|
||||
};
|
||||
let search_str = self.buffer.slice_to_cow(start..end);
|
||||
let search_case_sensitive =
|
||||
let case_sensitive = self.find.borrow().case_sensitive();
|
||||
let multicursor_case_sensitive =
|
||||
config.editor.multicursor_case_sensitive;
|
||||
let case_sensitive =
|
||||
multicursor_case_sensitive || case_sensitive;
|
||||
let search_whole_word =
|
||||
config.editor.multicursor_whole_words;
|
||||
let mut find = Find::new(0);
|
||||
find.set_find(
|
||||
&search_str,
|
||||
search_case_sensitive,
|
||||
false,
|
||||
search_whole_word,
|
||||
);
|
||||
find.set_case_sensitive(case_sensitive);
|
||||
find.set_find(&search_str, false, search_whole_word);
|
||||
let mut offset = 0;
|
||||
while let Some((start, end)) =
|
||||
find.next(self.buffer.text(), offset, false, false)
|
||||
|
@ -1347,17 +1346,15 @@ pub fn do_multi_selection(
|
|||
let r = selection.last_inserted().unwrap();
|
||||
let search_str =
|
||||
self.buffer.slice_to_cow(r.min()..r.max());
|
||||
let search_case_sensitive =
|
||||
config.editor.multicursor_case_sensitive;
|
||||
let case_sensitive = self.find.borrow().case_sensitive();
|
||||
let case_sensitive =
|
||||
config.editor.multicursor_case_sensitive
|
||||
|| case_sensitive;
|
||||
let search_whole_word =
|
||||
config.editor.multicursor_whole_words;
|
||||
let mut find = Find::new(0);
|
||||
find.set_find(
|
||||
&search_str,
|
||||
search_case_sensitive,
|
||||
false,
|
||||
search_whole_word,
|
||||
);
|
||||
find.set_case_sensitive(case_sensitive);
|
||||
find.set_find(&search_str, false, search_whole_word);
|
||||
let mut offset = r.max();
|
||||
let mut seen = HashSet::new();
|
||||
while let Some((start, end)) =
|
||||
|
@ -1396,8 +1393,10 @@ pub fn do_multi_selection(
|
|||
} else {
|
||||
let search_str =
|
||||
self.buffer.slice_to_cow(r.min()..r.max());
|
||||
let case_sensitive = self.find.borrow().case_sensitive();
|
||||
let mut find = Find::new(0);
|
||||
find.set_find(&search_str, false, false, false);
|
||||
find.set_case_sensitive(case_sensitive);
|
||||
find.set_find(&search_str, false, false);
|
||||
let mut offset = r.max();
|
||||
let mut seen = HashSet::new();
|
||||
while let Some((start, end)) =
|
||||
|
|
|
@ -1559,7 +1559,8 @@ fn run_focus_command(
|
|||
LapceUICommand::UpdateSearchInput(word.clone()),
|
||||
Target::Widget(*self.main_split.tab_id),
|
||||
));
|
||||
Arc::make_mut(&mut self.find).set_find(&word, false, false, true);
|
||||
|
||||
Arc::make_mut(&mut self.find).set_find(&word, false, true);
|
||||
let next =
|
||||
self.find
|
||||
.next(self.doc.buffer().text(), offset, false, true);
|
||||
|
@ -1634,6 +1635,22 @@ fn run_focus_command(
|
|||
}
|
||||
}
|
||||
}
|
||||
ToggleCaseSensitive => {
|
||||
let tab_id = *self.main_split.tab_id;
|
||||
let find = Arc::make_mut(&mut self.find);
|
||||
if let Some(pattern) = find.search_string.clone() {
|
||||
let case_sensitive = find.toggle_case_sensitive();
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::UpdateSearchWithCaseSensitivity {
|
||||
pattern,
|
||||
case_sensitive,
|
||||
},
|
||||
Target::Widget(tab_id),
|
||||
));
|
||||
}
|
||||
return CommandExecuted::No;
|
||||
}
|
||||
GlobalSearchRefresh => {
|
||||
let tab_id = *self.main_split.tab_id;
|
||||
let pattern = self.doc.buffer().to_string();
|
||||
|
@ -2136,8 +2153,7 @@ fn run_focus_command(
|
|||
.to_string()
|
||||
};
|
||||
if !pattern.contains('\n') {
|
||||
Arc::make_mut(&mut self.find)
|
||||
.set_find(&pattern, false, false, false);
|
||||
Arc::make_mut(&mut self.find).set_find(&pattern, false, false);
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::UpdateSearchInput(pattern),
|
||||
|
|
|
@ -107,6 +107,26 @@ pub fn set_hls_dirty(&mut self, is_dirty: bool) {
|
|||
self.hls_dirty = is_dirty
|
||||
}
|
||||
|
||||
/// Returns `true` if case sensitive, otherwise `false`
|
||||
pub fn case_sensitive(&self) -> bool {
|
||||
match self.case_matching {
|
||||
CaseMatching::Exact => true,
|
||||
CaseMatching::CaseInsensitive => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// FLips the current case sensitivity and return the new sensitivity
|
||||
/// `true` for case_sensitive, `false` for case insensitive.
|
||||
pub fn toggle_case_sensitive(&mut self) -> bool {
|
||||
let case_matching = match self.case_matching {
|
||||
CaseMatching::Exact => CaseMatching::CaseInsensitive,
|
||||
CaseMatching::CaseInsensitive => CaseMatching::Exact,
|
||||
};
|
||||
|
||||
self.case_matching = case_matching;
|
||||
self.case_sensitive()
|
||||
}
|
||||
|
||||
/// Returns `true` if the search query is a multi-line regex.
|
||||
pub(crate) fn is_multiline_regex(&self) -> bool {
|
||||
self.regex.is_some()
|
||||
|
@ -120,40 +140,42 @@ pub fn unset(&mut self) {
|
|||
self.hls_dirty = true;
|
||||
}
|
||||
|
||||
/// Sets find parameters and search query. Returns `true` if parameters have been updated.
|
||||
/// Returns `false` to indicate that parameters haven't change.
|
||||
pub fn set_find(
|
||||
&mut self,
|
||||
search_string: &str,
|
||||
case_sensitive: bool,
|
||||
is_regex: bool,
|
||||
whole_words: bool,
|
||||
) -> bool {
|
||||
if search_string.is_empty() {
|
||||
self.unset();
|
||||
}
|
||||
|
||||
/// Sets find case sensitivity.
|
||||
pub fn set_case_sensitive(&mut self, case_sensitive: bool) {
|
||||
let case_matching = if case_sensitive {
|
||||
CaseMatching::Exact
|
||||
} else {
|
||||
CaseMatching::CaseInsensitive
|
||||
};
|
||||
|
||||
if self.case_matching != case_matching {
|
||||
self.case_matching = case_matching;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets find parameters and search query. Returns `true` if parameters have been updated.
|
||||
/// Returns `false` to indicate that parameters haven't change.
|
||||
pub fn set_find(
|
||||
&mut self,
|
||||
search_string: &str,
|
||||
is_regex: bool,
|
||||
whole_words: bool,
|
||||
) {
|
||||
if search_string.is_empty() {
|
||||
self.unset();
|
||||
}
|
||||
if let Some(ref s) = self.search_string {
|
||||
if s == search_string
|
||||
&& case_matching == self.case_matching
|
||||
&& self.regex.is_some() == is_regex
|
||||
&& self.whole_words == whole_words
|
||||
{
|
||||
// search parameters did not change
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
self.unset();
|
||||
|
||||
self.search_string = Some(search_string.to_string());
|
||||
self.case_matching = case_matching;
|
||||
self.whole_words = whole_words;
|
||||
|
||||
// create regex from untrusted input
|
||||
|
@ -161,12 +183,10 @@ pub fn set_find(
|
|||
false => None,
|
||||
true => RegexBuilder::new(search_string)
|
||||
.size_limit(REGEX_SIZE_LIMIT)
|
||||
.case_insensitive(case_matching == CaseMatching::CaseInsensitive)
|
||||
.case_insensitive(!self.case_sensitive())
|
||||
.build()
|
||||
.ok(),
|
||||
};
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn next(
|
||||
|
|
|
@ -697,7 +697,7 @@ pub fn select(&mut self, ctx: &mut EventCtx) {
|
|||
let pattern = self.palette.get_input().to_string();
|
||||
let find = Arc::make_mut(&mut self.find);
|
||||
find.visual = true;
|
||||
find.set_find(&pattern, false, false, false);
|
||||
find.set_find(&pattern, false, false);
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::UpdateSearchInput(pattern),
|
||||
|
|
|
@ -292,7 +292,10 @@ fn handle_request(&mut self, id: RequestId, rpc: ProxyRequest) {
|
|||
};
|
||||
self.respond_rpc(id, result);
|
||||
}
|
||||
GlobalSearch { pattern } => {
|
||||
GlobalSearch {
|
||||
pattern,
|
||||
case_sensitive,
|
||||
} => {
|
||||
let workspace = self.workspace.clone();
|
||||
let proxy_rpc = self.proxy_rpc.clone();
|
||||
thread::spawn(move || {
|
||||
|
@ -300,7 +303,7 @@ fn handle_request(&mut self, id: RequestId, rpc: ProxyRequest) {
|
|||
let mut matches = HashMap::new();
|
||||
let pattern = regex::escape(&pattern);
|
||||
if let Ok(matcher) = RegexMatcherBuilder::new()
|
||||
.case_insensitive(true)
|
||||
.case_insensitive(!case_sensitive)
|
||||
.build_literals(&[&pattern])
|
||||
{
|
||||
let mut searcher = SearcherBuilder::new().build();
|
||||
|
|
|
@ -48,6 +48,7 @@ pub enum ProxyRequest {
|
|||
},
|
||||
GlobalSearch {
|
||||
pattern: String,
|
||||
case_sensitive: bool,
|
||||
},
|
||||
CompletionResolve {
|
||||
plugin_id: PluginId,
|
||||
|
@ -576,8 +577,19 @@ pub fn save_buffer_as(
|
|||
);
|
||||
}
|
||||
|
||||
pub fn global_search(&self, pattern: String, f: impl ProxyCallback + 'static) {
|
||||
self.request_async(ProxyRequest::GlobalSearch { pattern }, f);
|
||||
pub fn global_search(
|
||||
&self,
|
||||
pattern: String,
|
||||
case_sensitive: bool,
|
||||
f: impl ProxyCallback + 'static,
|
||||
) {
|
||||
self.request_async(
|
||||
ProxyRequest::GlobalSearch {
|
||||
pattern,
|
||||
case_sensitive,
|
||||
},
|
||||
f,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn save(&self, rev: u64, path: PathBuf, f: impl ProxyCallback + 'static) {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
use crate::{editor::view::LapceEditorView, svg::get_svg, tab::LapceIcon};
|
||||
|
||||
const CASE_SENSITIVE_ICON: &str = "case-sensitive.svg";
|
||||
|
||||
pub struct FindBox {
|
||||
parent_view_id: WidgetId,
|
||||
input_width: f64,
|
||||
|
@ -57,6 +59,18 @@ pub fn new(
|
|||
Target::Widget(parent_view_id),
|
||||
),
|
||||
},
|
||||
LapceIcon {
|
||||
icon: CASE_SENSITIVE_ICON,
|
||||
rect: Rect::ZERO,
|
||||
command: Command::new(
|
||||
LAPCE_COMMAND,
|
||||
LapceCommand {
|
||||
kind: CommandKind::Focus(FocusCommand::ToggleCaseSensitive),
|
||||
data: None,
|
||||
},
|
||||
Target::Widget(parent_view_id),
|
||||
),
|
||||
},
|
||||
LapceIcon {
|
||||
icon: "close.svg",
|
||||
rect: Rect::ZERO,
|
||||
|
@ -136,16 +150,17 @@ fn layout(
|
|||
BoxConstraints::tight(Size::new(self.input_width, bc.max().height));
|
||||
let mut input_size = self.input.layout(ctx, &input_bc, data, env);
|
||||
self.input.set_origin(ctx, data, env, Point::ZERO);
|
||||
let icons_len = self.icons.len() as f64;
|
||||
let height = input_size.height;
|
||||
let mut width = input_size.width + self.result_width + height * 3.0;
|
||||
let mut width = input_size.width + self.result_width + height * icons_len;
|
||||
|
||||
if width - 20.0 > bc.max().width {
|
||||
let input_bc = BoxConstraints::tight(Size::new(
|
||||
bc.max().width - height * 3.0 - 20.0 - self.result_width,
|
||||
bc.max().width - height * icons_len - 20.0 - self.result_width,
|
||||
bc.max().height,
|
||||
));
|
||||
input_size = self.input.layout(ctx, &input_bc, data, env);
|
||||
width = input_size.width + self.result_width + height * 3.0;
|
||||
width = input_size.width + self.result_width + height * icons_len;
|
||||
}
|
||||
|
||||
for (i, icon) in self.icons.iter_mut().enumerate() {
|
||||
|
@ -260,8 +275,25 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceTabData, env: &Env) {
|
|||
Point::new(input_size.width, text_layout.y_offset(input_size.height)),
|
||||
);
|
||||
|
||||
let case_sensitive = data
|
||||
.main_split
|
||||
.active_editor()
|
||||
.map(|editor| {
|
||||
let editor_data = data.editor_view_content(editor.view_id);
|
||||
editor_data.find.case_sensitive()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
for icon in self.icons.iter() {
|
||||
if icon.rect.contains(self.mouse_pos) {
|
||||
if icon.icon == CASE_SENSITIVE_ICON && case_sensitive {
|
||||
ctx.fill(
|
||||
&icon.rect,
|
||||
data.config
|
||||
.get_color_unchecked(LapceTheme::LAPCE_ACTIVE_TAB),
|
||||
);
|
||||
} else if icon.rect.contains(self.mouse_pos)
|
||||
&& icon.icon != CASE_SENSITIVE_ICON
|
||||
{
|
||||
ctx.fill(
|
||||
&icon.rect,
|
||||
data.config
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
use druid::{
|
||||
piet::{Text, TextAttribute, TextLayout as PietTextLayout, TextLayoutBuilder},
|
||||
BoxConstraints, Command, Cursor, Data, Env, Event, EventCtx, FontWeight,
|
||||
LayoutCtx, LifeCycle, LifeCycleCtx, MouseEvent, PaintCtx, Point, RenderContext,
|
||||
Size, Target, UpdateCtx, Widget, WidgetExt, WidgetId,
|
||||
LayoutCtx, LifeCycle, LifeCycleCtx, MouseEvent, PaintCtx, Point, Rect,
|
||||
RenderContext, Size, Target, UpdateCtx, Widget, WidgetExt, WidgetId, WidgetPod,
|
||||
};
|
||||
use lapce_core::command::FocusCommand;
|
||||
use lapce_data::command::{CommandKind, LapceCommand, LAPCE_COMMAND};
|
||||
use lapce_data::{
|
||||
command::{LapceUICommand, LAPCE_UI_COMMAND},
|
||||
config::LapceTheme,
|
||||
|
@ -14,6 +16,8 @@
|
|||
panel::PanelKind,
|
||||
};
|
||||
|
||||
use crate::svg::get_svg;
|
||||
use crate::tab::LapceIcon;
|
||||
use crate::{
|
||||
editor::view::LapceEditorView,
|
||||
panel::{LapcePanel, PanelHeaderKind, PanelSizing},
|
||||
|
@ -22,19 +26,276 @@
|
|||
svg::file_svg,
|
||||
};
|
||||
|
||||
pub struct SearchInput {
|
||||
input: WidgetPod<LapceTabData, Box<dyn Widget<LapceTabData>>>,
|
||||
icons: Vec<LapceIcon>,
|
||||
parent_view_id: WidgetId,
|
||||
result_width: f64,
|
||||
search_input_padding: f64,
|
||||
mouse_pos: Point,
|
||||
}
|
||||
|
||||
impl SearchInput {
|
||||
fn new(view_id: WidgetId) -> Self {
|
||||
let id = WidgetId::next();
|
||||
|
||||
let search_input_padding = 15.0;
|
||||
let input = LapceEditorView::new(view_id, id, None)
|
||||
.hide_header()
|
||||
.hide_gutter()
|
||||
.padding((search_input_padding, search_input_padding));
|
||||
|
||||
let icons = vec![LapceIcon {
|
||||
icon: "case-sensitive.svg",
|
||||
rect: Rect::ZERO,
|
||||
command: Command::new(
|
||||
LAPCE_COMMAND,
|
||||
LapceCommand {
|
||||
kind: CommandKind::Focus(FocusCommand::ToggleCaseSensitive),
|
||||
data: None,
|
||||
},
|
||||
Target::Widget(view_id),
|
||||
),
|
||||
}];
|
||||
|
||||
Self {
|
||||
parent_view_id: view_id,
|
||||
result_width: 75.0,
|
||||
input: WidgetPod::new(input.boxed()),
|
||||
icons,
|
||||
mouse_pos: Point::ZERO,
|
||||
search_input_padding,
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_down(&self, ctx: &mut EventCtx, mouse_event: &MouseEvent) {
|
||||
for icon in self.icons.iter() {
|
||||
if icon.rect.contains(mouse_event.pos) {
|
||||
ctx.submit_command(icon.command.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn icon_hit_test(&self, mouse_event: &MouseEvent) -> bool {
|
||||
for icon in self.icons.iter() {
|
||||
if icon.rect.contains(mouse_event.pos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget<LapceTabData> for SearchInput {
|
||||
fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
event: &Event,
|
||||
data: &mut LapceTabData,
|
||||
env: &Env,
|
||||
) {
|
||||
self.input.event(ctx, event, data, env);
|
||||
match event {
|
||||
Event::MouseMove(mouse_event) => {
|
||||
ctx.set_handled();
|
||||
self.mouse_pos = mouse_event.pos;
|
||||
if self.icon_hit_test(mouse_event) {
|
||||
ctx.set_cursor(&druid::Cursor::Pointer);
|
||||
} else {
|
||||
ctx.clear_cursor();
|
||||
}
|
||||
}
|
||||
Event::MouseDown(mouse_event) => {
|
||||
ctx.set_handled();
|
||||
self.mouse_down(ctx, mouse_event);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
ctx: &mut LayoutCtx,
|
||||
bc: &BoxConstraints,
|
||||
data: &LapceTabData,
|
||||
env: &Env,
|
||||
) -> Size {
|
||||
let input_bc = BoxConstraints::tight(bc.max());
|
||||
let mut input_size = self.input.layout(ctx, &input_bc, data, env);
|
||||
self.input.set_origin(ctx, data, env, Point::ZERO);
|
||||
let icon_len = self.icons.len() as f64;
|
||||
let height = input_size.height;
|
||||
let icon_height = height - self.search_input_padding;
|
||||
let mut width = input_size.width + self.result_width + height * icon_len;
|
||||
|
||||
if width - 20.0 > bc.max().width {
|
||||
let input_bc = BoxConstraints::tight(Size::new(
|
||||
bc.max().width - height * icon_len - 20.0 - self.result_width,
|
||||
bc.max().height,
|
||||
));
|
||||
input_size = self.input.layout(ctx, &input_bc, data, env);
|
||||
width = input_size.width + self.result_width + height * icon_len;
|
||||
}
|
||||
|
||||
for (i, icon) in self.icons.iter_mut().enumerate() {
|
||||
icon.rect = Size::new(icon_height, icon_height)
|
||||
.to_rect()
|
||||
.with_origin(Point::new(
|
||||
input_size.width + self.result_width + i as f64 * icon_height,
|
||||
self.search_input_padding / 2.0,
|
||||
))
|
||||
.inflate(-5.0, -5.0);
|
||||
}
|
||||
|
||||
Size::new(width, height)
|
||||
}
|
||||
|
||||
fn lifecycle(
|
||||
&mut self,
|
||||
ctx: &mut LifeCycleCtx,
|
||||
event: &LifeCycle,
|
||||
data: &LapceTabData,
|
||||
env: &Env,
|
||||
) {
|
||||
self.input.lifecycle(ctx, event, data, env);
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
ctx: &mut UpdateCtx,
|
||||
_old_data: &LapceTabData,
|
||||
data: &LapceTabData,
|
||||
env: &Env,
|
||||
) {
|
||||
self.input.update(ctx, data, env);
|
||||
}
|
||||
|
||||
fn paint(&mut self, ctx: &mut PaintCtx, data: &LapceTabData, env: &Env) {
|
||||
let buffer = data.editor_view_content(self.parent_view_id);
|
||||
|
||||
let rect = ctx.size().to_rect();
|
||||
ctx.with_save(|ctx| {
|
||||
ctx.clip(rect.inset((100.0, 0.0, 100.0, 100.0)));
|
||||
let shadow_width = data.config.ui.drop_shadow_width() as f64;
|
||||
if shadow_width > 0.0 {
|
||||
ctx.blurred_rect(
|
||||
rect,
|
||||
shadow_width,
|
||||
data.config
|
||||
.get_color_unchecked(LapceTheme::LAPCE_DROPDOWN_SHADOW),
|
||||
);
|
||||
} else {
|
||||
ctx.stroke(
|
||||
rect.inflate(0.5, 0.5),
|
||||
data.config.get_color_unchecked(LapceTheme::LAPCE_BORDER),
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
});
|
||||
ctx.fill(
|
||||
rect,
|
||||
data.config
|
||||
.get_color_unchecked(LapceTheme::EDITOR_BACKGROUND),
|
||||
);
|
||||
self.input.paint(ctx, data, env);
|
||||
|
||||
let mut index = None;
|
||||
let cursor_offset = buffer.editor.cursor.offset();
|
||||
|
||||
for i in 0..buffer.doc.find.borrow().occurrences().regions().len() {
|
||||
let region = buffer.doc.find.borrow().occurrences().regions()[i];
|
||||
if region.min() <= cursor_offset && cursor_offset <= region.max() {
|
||||
index = Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
let match_count = data
|
||||
.search
|
||||
.matches
|
||||
.iter()
|
||||
.map(|(_, matches)| matches.len())
|
||||
.sum::<usize>();
|
||||
|
||||
let text_layout = ctx
|
||||
.text()
|
||||
.new_text_layout(if match_count > 0 {
|
||||
match index {
|
||||
Some(index) => format!("{}/{}", index + 1, match_count),
|
||||
None => format!("{} results", match_count),
|
||||
}
|
||||
} else {
|
||||
"No results".to_string()
|
||||
})
|
||||
.font(
|
||||
data.config.ui.font_family(),
|
||||
data.config.ui.font_size() as f64,
|
||||
)
|
||||
.text_color(
|
||||
data.config
|
||||
.get_color_unchecked(LapceTheme::EDITOR_FOREGROUND)
|
||||
.clone(),
|
||||
)
|
||||
.max_width(self.result_width)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let input_size = self.input.layout_rect().size();
|
||||
ctx.draw_text(
|
||||
&text_layout,
|
||||
Point::new(input_size.width, text_layout.y_offset(input_size.height)),
|
||||
);
|
||||
|
||||
let case_sensitive = data
|
||||
.main_split
|
||||
.active_editor()
|
||||
.map(|editor| {
|
||||
let editor_data = data.editor_view_content(editor.view_id);
|
||||
editor_data.find.case_sensitive()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
for icon in self.icons.iter() {
|
||||
if icon.icon == "case-sensitive.svg" && case_sensitive {
|
||||
ctx.fill(
|
||||
&icon.rect,
|
||||
data.config
|
||||
.get_color_unchecked(LapceTheme::LAPCE_ACTIVE_TAB),
|
||||
);
|
||||
} else if icon.rect.contains(self.mouse_pos)
|
||||
&& icon.icon != "case-sensitive.svg"
|
||||
{
|
||||
ctx.fill(
|
||||
&icon.rect,
|
||||
data.config
|
||||
.get_color_unchecked(LapceTheme::EDITOR_CURRENT_LINE),
|
||||
);
|
||||
}
|
||||
|
||||
let svg = get_svg(icon.icon).unwrap();
|
||||
ctx.draw_svg(
|
||||
&svg,
|
||||
icon.rect.inflate(-7.0, -7.0),
|
||||
Some(
|
||||
data.config
|
||||
.get_color_unchecked(LapceTheme::EDITOR_FOREGROUND),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_search_panel(data: &LapceTabData) -> LapcePanel {
|
||||
let editor_data = data
|
||||
.main_split
|
||||
.editors
|
||||
.get(&data.search.editor_view_id)
|
||||
.unwrap();
|
||||
let input = LapceEditorView::new(editor_data.view_id, WidgetId::next(), None)
|
||||
.hide_header()
|
||||
.hide_gutter()
|
||||
.padding((15.0, 15.0));
|
||||
|
||||
let search_bar = SearchInput::new(editor_data.view_id);
|
||||
|
||||
let split = LapceSplit::new(data.search.split_id)
|
||||
.horizontal()
|
||||
.with_child(input.boxed(), None, 100.0)
|
||||
.with_child(search_bar.boxed(), None, 100.0)
|
||||
.with_flex_child(
|
||||
LapceScroll::new(SearchContent::new().boxed())
|
||||
.vertical()
|
||||
|
@ -44,6 +305,7 @@ pub fn new_search_panel(data: &LapceTabData) -> LapcePanel {
|
|||
false,
|
||||
)
|
||||
.hide_border();
|
||||
|
||||
LapcePanel::new(
|
||||
PanelKind::Search,
|
||||
data.search.widget_id,
|
||||
|
@ -174,6 +436,7 @@ fn layout(
|
|||
.iter()
|
||||
.map(|(_, matches)| matches.len() + 1)
|
||||
.sum::<usize>();
|
||||
|
||||
let height = self.line_height * n as f64;
|
||||
Size::new(bc.max().width, height)
|
||||
}
|
||||
|
|
|
@ -787,14 +787,18 @@ fn handle_command_event(
|
|||
Arc::make_mut(doc).reload(Rope::from(pattern), true);
|
||||
}
|
||||
}
|
||||
LapceUICommand::UpdateSearch(pattern) => {
|
||||
LapceUICommand::UpdateSearchWithCaseSensitivity {
|
||||
pattern,
|
||||
case_sensitive,
|
||||
} => {
|
||||
if pattern.is_empty() {
|
||||
Arc::make_mut(&mut data.find).unset();
|
||||
Arc::make_mut(&mut data.search).matches =
|
||||
Arc::new(HashMap::new());
|
||||
} else {
|
||||
let find = Arc::make_mut(&mut data.find);
|
||||
find.set_find(pattern, false, false, false);
|
||||
find.set_case_sensitive(*case_sensitive);
|
||||
find.set_find(pattern, false, false);
|
||||
find.visual = true;
|
||||
if data.focus_area == FocusArea::Panel(PanelKind::Search)
|
||||
{
|
||||
|
@ -816,6 +820,7 @@ fn handle_command_event(
|
|||
let tab_id = data.id;
|
||||
data.proxy.proxy_rpc.global_search(
|
||||
pattern.clone(),
|
||||
find.case_sensitive(),
|
||||
Box::new(move |result| {
|
||||
if let Ok(
|
||||
ProxyResponse::GlobalSearchResponse {
|
||||
|
@ -836,6 +841,17 @@ fn handle_command_event(
|
|||
)
|
||||
}
|
||||
}
|
||||
LapceUICommand::UpdateSearch(pattern) => {
|
||||
let case_sensitive = data.find.case_sensitive();
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::UpdateSearchWithCaseSensitivity {
|
||||
pattern: pattern.clone(),
|
||||
case_sensitive,
|
||||
},
|
||||
Target::Widget(self.id),
|
||||
))
|
||||
}
|
||||
LapceUICommand::OpenPluginInfo(volt) => {
|
||||
data.main_split.open_plugin_info(ctx, volt);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue