mirror of
https://github.com/servo/servo.git
synced 2025-08-18 11:55:39 +01:00
Basic tab strip for the minibrowser (#33100)
This implements a simple tab system for servoshell: - The egui part uses the built-in SelectableLabels components and display the full tab title on hover. - WebView structs now hold all the state for each WebView. When we need "global" state, we return the focused WebView state, eg. for the load status since it's still global in the UI. - New keyboard shortcut: [Cmd-or-Ctrl]+[W] to close the current tab. - New keyboard shortcut: [Cmd-or-Ctrl]+[T] to create a new tab. - The new tab content is loaded from the 'servo:newtab' url using a couple of custom protocol handlers. Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
parent
a0ff57cea1
commit
1b48bd18aa
14 changed files with 455 additions and 72 deletions
|
@ -7,9 +7,11 @@ use std::num::NonZeroU32;
|
|||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use egui::text::{CCursor, CCursorRange};
|
||||
use egui::text_edit::TextEditState;
|
||||
use egui::{
|
||||
pos2, CentralPanel, Color32, Frame, Key, Label, Modifiers, PaintCallback, Pos2, TopBottomPanel,
|
||||
Vec2,
|
||||
pos2, CentralPanel, Color32, Frame, Key, Label, Modifiers, PaintCallback, Pos2,
|
||||
SelectableLabel, TopBottomPanel, Vec2,
|
||||
};
|
||||
use egui_glow::CallbackFn;
|
||||
use egui_winit::EventResponse;
|
||||
|
@ -61,6 +63,15 @@ pub enum MinibrowserEvent {
|
|||
Reload,
|
||||
}
|
||||
|
||||
fn truncate_with_ellipsis(input: &str, max_length: usize) -> String {
|
||||
if input.chars().count() > max_length {
|
||||
let truncated: String = input.chars().take(max_length.saturating_sub(1)).collect();
|
||||
format!("{}…", truncated)
|
||||
} else {
|
||||
input.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Minibrowser {
|
||||
pub fn new(
|
||||
rendering_context: &RenderingContext,
|
||||
|
@ -216,9 +227,11 @@ impl Minibrowser {
|
|||
ui.available_size(),
|
||||
egui::Layout::right_to_left(egui::Align::Center),
|
||||
|ui| {
|
||||
let location_id = egui::Id::new("location_input");
|
||||
let location_field = ui.add_sized(
|
||||
ui.available_size(),
|
||||
egui::TextEdit::singleline(&mut *location.borrow_mut()),
|
||||
egui::TextEdit::singleline(&mut *location.borrow_mut())
|
||||
.id(location_id),
|
||||
);
|
||||
|
||||
if location_field.changed() {
|
||||
|
@ -228,6 +241,16 @@ impl Minibrowser {
|
|||
i.clone().consume_key(Modifiers::COMMAND, Key::L)
|
||||
}) {
|
||||
location_field.request_focus();
|
||||
if let Some(mut state) =
|
||||
TextEditState::load(ui.ctx(), location_id)
|
||||
{
|
||||
// Select the whole input.
|
||||
state.cursor.set_char_range(Some(CCursorRange::two(
|
||||
CCursor::new(0),
|
||||
CCursor::new(location.borrow().len()),
|
||||
)));
|
||||
state.store(ui.ctx(), location_id);
|
||||
}
|
||||
}
|
||||
if location_field.lost_focus() &&
|
||||
ui.input(|i| i.clone().key_pressed(Key::Enter))
|
||||
|
@ -242,6 +265,36 @@ impl Minibrowser {
|
|||
});
|
||||
};
|
||||
|
||||
let mut embedder_events = vec![];
|
||||
|
||||
// A simple Tab header strip, using egui 'SelectableLabel' elements.
|
||||
// TODO: Add a way to close a tab eg. with a [x] control.
|
||||
TopBottomPanel::top("tabs").show(ctx, |ui| {
|
||||
ui.allocate_ui_with_layout(
|
||||
ui.available_size(),
|
||||
egui::Layout::left_to_right(egui::Align::Center),
|
||||
|ui| {
|
||||
for (webview_id, webview) in webviews.webviews().into_iter() {
|
||||
let msg = match (webview.title.clone(), webview.url.clone()) {
|
||||
(Some(title), _) => title,
|
||||
(None, Some(url)) => url.to_string(),
|
||||
_ => "".to_owned(),
|
||||
};
|
||||
let tab = ui.add(SelectableLabel::new(
|
||||
webview.focused,
|
||||
truncate_with_ellipsis(&msg, 20),
|
||||
));
|
||||
let tab = tab.on_hover_ui(|ui| {
|
||||
ui.label(&msg);
|
||||
});
|
||||
if !webview.focused && tab.clicked() {
|
||||
embedder_events.push(EmbedderEvent::FocusWebView(webview_id));
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// The toolbar height is where the Context’s available rect starts.
|
||||
// For reasons that are unclear, the TopBottomPanel’s ui cursor exceeds this by one egui
|
||||
// point, but the Context is correct and the TopBottomPanel is wrong.
|
||||
|
@ -255,7 +308,6 @@ impl Minibrowser {
|
|||
let Some(webview) = webviews.get_mut(focused_webview_id) else {
|
||||
return;
|
||||
};
|
||||
let mut embedder_events = vec![];
|
||||
|
||||
CentralPanel::default()
|
||||
.frame(Frame::none())
|
||||
|
@ -362,9 +414,9 @@ impl Minibrowser {
|
|||
app_event_queue: &mut Vec<EmbedderEvent>,
|
||||
) {
|
||||
for event in self.event_queue.borrow_mut().drain(..) {
|
||||
let browser_id = browser.focused_webview_id().unwrap();
|
||||
match event {
|
||||
MinibrowserEvent::Go => {
|
||||
let browser_id = browser.webview_id().unwrap();
|
||||
let location = self.location.borrow();
|
||||
if let Some(url) = location_bar_input_to_url(&location.clone()) {
|
||||
app_event_queue.push(EmbedderEvent::LoadUrl(browser_id, url));
|
||||
|
@ -374,21 +426,19 @@ impl Minibrowser {
|
|||
}
|
||||
},
|
||||
MinibrowserEvent::Back => {
|
||||
let browser_id = browser.webview_id().unwrap();
|
||||
app_event_queue.push(EmbedderEvent::Navigation(
|
||||
browser_id,
|
||||
TraversalDirection::Back(1),
|
||||
));
|
||||
},
|
||||
MinibrowserEvent::Forward => {
|
||||
let browser_id = browser.webview_id().unwrap();
|
||||
app_event_queue.push(EmbedderEvent::Navigation(
|
||||
browser_id,
|
||||
TraversalDirection::Forward(1),
|
||||
));
|
||||
},
|
||||
MinibrowserEvent::Reload => {
|
||||
let browser_id = browser.webview_id().unwrap();
|
||||
let browser_id = browser.focused_webview_id().unwrap();
|
||||
app_event_queue.push(EmbedderEvent::Reload(browser_id));
|
||||
},
|
||||
}
|
||||
|
@ -407,7 +457,7 @@ impl Minibrowser {
|
|||
}
|
||||
|
||||
match browser.current_url_string() {
|
||||
Some(location) if location != self.location.get_mut() => {
|
||||
Some(location) if location != *self.location.get_mut() => {
|
||||
self.location = RefCell::new(location.to_owned());
|
||||
true
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue