Skip to content

Commit 87b8dae

Browse files
committed
support egui top bar search
1 parent f22a8b8 commit 87b8dae

File tree

8 files changed

+77
-40
lines changed

8 files changed

+77
-40
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

asm_egui/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ egui_extras = { workspace = true }
2222
egui_flex = { workspace = true }
2323

2424
fontdb = { workspace = true }
25-
rfd = { workspace = true }
25+
bit-set = { workspace = true }
2626

2727
[profile.release]
2828
strip = true

asm_egui/src/smali.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -281,17 +281,17 @@ fn simple_text(
281281
}
282282

283283
#[derive(Copy, Clone, Debug)]
284-
struct SmaliStyle {
285-
op: Color32,
286-
offset: Color32,
287-
register: Color32,
288-
desc: Color32,
289-
literal: Color32,
290-
highlight: Color32,
284+
pub(crate) struct SmaliStyle {
285+
pub op: Color32,
286+
pub offset: Color32,
287+
pub register: Color32,
288+
pub desc: Color32,
289+
pub literal: Color32,
290+
pub highlight: Color32,
291291
}
292292

293293
impl SmaliStyle {
294-
const LIGHT: SmaliStyle = SmaliStyle {
294+
pub(crate) const LIGHT: SmaliStyle = SmaliStyle {
295295
op: Color32::from_rgb(235, 0, 0),
296296
offset: Color32::from_rgb(96, 96, 96),
297297
register: Color32::from_rgb(83, 141, 199),
@@ -300,7 +300,7 @@ impl SmaliStyle {
300300
highlight: Color32::from_rgb(255, 199, 133),
301301
};
302302

303-
const DARK: SmaliStyle = SmaliStyle {
303+
pub(crate) const DARK: SmaliStyle = SmaliStyle {
304304
op: Color32::from_rgb(255, 100, 100),
305305
offset: SmaliStyle::LIGHT.offset,
306306
register: SmaliStyle::LIGHT.register,

asm_egui/src/top_bar.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::app::EguiApp;
2+
use bit_set::BitSet;
23
use egui::containers::PopupCloseBehavior;
3-
use egui::{popup_below_widget, Context, Id, Response, TextEdit, Ui};
4+
use egui::text::LayoutJob;
5+
use egui::{popup_below_widget, Context, Id, Response, TextEdit, TextFormat, TextStyle, Ui};
46
use java_asm_server::AsmServer;
57
use std::ops::Deref;
68

@@ -58,7 +60,7 @@ impl EguiApp {
5860
let search_results = locked_top.search_result.clone();
5961
drop(locked_top);
6062

61-
if search_results.is_empty() { return; }
63+
if search_results.items.is_empty() { return; }
6264
popup_below_widget(
6365
ui, popup_id, &edit_path_ui,
6466
PopupCloseBehavior::CloseOnClickOutside, |ui| {
@@ -88,13 +90,65 @@ impl EguiApp {
8890

8991
fn popup_file_path_ui(&mut self, ui: &mut Ui) {
9092
let search_results = self.server_app.top().lock().search_result.clone();
91-
for result in search_results {
92-
let selectable_label = ui.selectable_label(false, result.to_string());
93+
let style = ui.style();
94+
let font = TextStyle::Monospace.resolve(&style);
95+
96+
let dark_mode = style.visuals.dark_mode;
97+
let smali_style = if dark_mode { crate::smali::SmaliStyle::DARK } else { crate::smali::SmaliStyle::LIGHT };
98+
99+
let dft_color = style.visuals.text_color();
100+
let dft_text_format = TextFormat::simple(font.clone(), dft_color);
101+
let highlight_color = smali_style.desc;
102+
let highlight_text_format = TextFormat::simple(font, highlight_color);
103+
104+
105+
for result in search_results.items {
106+
let path = result.item.to_string();
107+
let sections = Self::get_highlight_sections(&path, result.indices);
108+
let mut text_layout_job = LayoutJob::default();
109+
for (section, highlighted) in sections {
110+
if highlighted {
111+
text_layout_job.append(&section, 0.0, highlight_text_format.clone())
112+
} else {
113+
text_layout_job.append(&section, 0.0, dft_text_format.clone())
114+
}
115+
}
116+
let selectable_label = ui.selectable_label(false, text_layout_job);
93117
if selectable_label.clicked() {
94118
let server_locked = self.server.lock();
95119
let Some(server) = server_locked.deref() else { return; };
96-
server.switch_or_open(&result, &self.server_app);
120+
server.switch_or_open(&result.item, &self.server_app);
121+
ui.memory_mut(|m| m.close_popup());
97122
}
98123
}
99124
}
125+
126+
fn get_highlight_sections(path: &str, bits: BitSet) -> Vec<(String, bool)> {
127+
let mut current_section = String::new();
128+
let mut cur_highlighted = false;
129+
130+
let mut sections = vec![];
131+
for (index, ch) in path.chars().enumerate() {
132+
let target_highlighted = bits.contains(index);
133+
if current_section.is_empty() {
134+
// first char in this section, init
135+
current_section.push(ch);
136+
cur_highlighted = target_highlighted;
137+
} else if cur_highlighted == target_highlighted {
138+
// same highlight
139+
current_section.push(ch);
140+
} else {
141+
// different color, start new section
142+
sections.push((current_section, cur_highlighted));
143+
current_section = ch.to_string();
144+
cur_highlighted = target_highlighted;
145+
}
146+
}
147+
148+
// last section
149+
if !current_section.is_empty() {
150+
sections.push((current_section, cur_highlighted));
151+
}
152+
sections
153+
}
100154
}

asm_server/src/impls/fuzzy.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ pub struct SearchResultItem {
198198
pub indices: BitSet,
199199
}
200200

201-
#[derive(Debug, Clone, PartialEq, Eq)]
201+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
202202
pub struct SearchResult {
203203
pub stop_idx: usize,
204204
pub items: Vec<SearchResultItem>,
@@ -299,12 +299,12 @@ mod tests {
299299
let start = std::time::Instant::now();
300300
let mut model = FuzzyMatchModel::new(input, &items, 10000);
301301
let result = model.full_search();
302-
println!("Cost time: {:?}ms for take 1000 items", start.elapsed().as_millis());
302+
println!("Cost time: {:?}ms for take 10000 items", start.elapsed().as_millis());
303303
assert_eq!(result.items.len(), 10000);
304304

305305
let start = std::time::Instant::now();
306306
let result = model.search_with_new_input("im21z".into());
307-
println!("Cost time: {:?}ms for take 1000 items next time", start.elapsed().as_millis());
307+
println!("Cost time: {:?}ms for take 10000 items next time", start.elapsed().as_millis());
308308
assert_eq!(result.items.len(), 10000);
309309
}
310310
}

asm_server/src/impls/server.rs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use zip::ZipArchive;
1515

1616
pub enum ServerMessage {
1717
Progress(ProgressMessage),
18-
SearchResult(SearchResultMessage),
1918
}
2019

2120
pub struct ProgressMessage {
@@ -24,12 +23,6 @@ pub struct ProgressMessage {
2423
pub in_loading: bool,
2524
}
2625

27-
// search is too fast now, it's meaningless to use the message to dispatch.
28-
// so we didn't use this message currently.
29-
pub struct SearchResultMessage {
30-
pub result: Vec<StrRef>,
31-
}
32-
3326
pub struct FileOpenContext {
3427
pub path: String,
3528
pub start_time: Instant,
@@ -55,9 +48,6 @@ impl AsmServer {
5548
server_ref.loading_state.in_loading = progress.in_loading;
5649
server_ref.on_progress_update(&render_target);
5750
}
58-
ServerMessage::SearchResult(result) => {
59-
Self::on_search_result(result, &render_target);
60-
}
6151
}
6252
}
6353
});
@@ -94,13 +84,6 @@ impl AsmServer {
9484
(*top_mut).loading_state = current_loading_state.clone();
9585
}
9686

97-
fn on_search_result(result: SearchResultMessage, render_target: &AppContainer) {
98-
let result = result.result;
99-
let mut top = render_target.top().lock();
100-
let top_mut = top.deref_mut();
101-
(*top_mut).search_result = result;
102-
}
103-
10487
fn render_to_app(&self, app: AppContainer) {
10588
let classes = self.read_classes();
10689
let start = Instant::now();

asm_server/src/server.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,8 @@ impl AsmServer {
126126
let query: StrRef = query.as_str().into();
127127
let mut fuzzy_locked = self.get_or_create_fuzzy(query.clone()).lock();
128128
let Some(fuzzy) = fuzzy_locked.deref_mut() else { return; };
129-
let results: Vec<StrRef> = fuzzy.search_with_new_input(query).items
130-
.into_iter().map(|item| item.item).collect();
131-
top.search_result = results;
129+
let search_result = fuzzy.search_with_new_input(query);
130+
top.search_result = search_result;
132131
}
133132
}
134133

asm_server/src/ui/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use std::collections::BTreeMap;
1313
use std::iter::{Enumerate, Peekable};
1414
use std::str::Split;
1515
use std::sync::Arc;
16+
use crate::impls::fuzzy::SearchResult;
1617

1718
/// contains all states of the app.
1819
/// It's not like the [crate::AsmServer] which only contains the information of a file.
@@ -41,7 +42,7 @@ impl AppContainer {
4142
pub struct Top {
4243
pub loading_state: LoadingState,
4344
pub file_path: Option<String>,
44-
pub search_result: Vec<StrRef>,
45+
pub search_result: SearchResult,
4546
}
4647

4748
#[derive(Default, Clone, Debug)]

0 commit comments

Comments
 (0)