mirror of
https://github.com/servo/servo.git
synced 2025-08-07 14:35:33 +01:00
servoshell: Add close buttons and increase interactivity of tabs (#33244)
* Improved the minibrowser tab bar Added a close button for each tab as well as another button for opening a new tab, also changed the styling so it looks more like other browsers. Signed-off-by: Benjamin Vincent Schulenburg <bennyschulenburg@gmx.de> * Make sure to restore the egui visuals after drawing a browser tab Signed-off-by: Benjamin Vincent Schulenburg <bennyschulenburg@gmx.de> * Only use colors from the current theme for the minibrowser tabbar That way we can easily switch between light and dark mode Signed-off-by: Benjamin Vincent Schulenburg <bennyschulenburg@gmx.de> --------- Signed-off-by: Benjamin Vincent Schulenburg <bennyschulenburg@gmx.de>
This commit is contained in:
parent
3c6ca33832
commit
891562be8e
1 changed files with 103 additions and 18 deletions
|
@ -10,8 +10,8 @@ use std::time::Instant;
|
||||||
use egui::text::{CCursor, CCursorRange};
|
use egui::text::{CCursor, CCursorRange};
|
||||||
use egui::text_edit::TextEditState;
|
use egui::text_edit::TextEditState;
|
||||||
use egui::{
|
use egui::{
|
||||||
pos2, CentralPanel, Color32, Frame, Key, Label, Modifiers, PaintCallback, Pos2,
|
pos2, CentralPanel, Frame, Key, Label, Modifiers, PaintCallback, Pos2, SelectableLabel,
|
||||||
SelectableLabel, TopBottomPanel, Vec2,
|
TopBottomPanel, Vec2,
|
||||||
};
|
};
|
||||||
use egui_glow::CallbackFn;
|
use egui_glow::CallbackFn;
|
||||||
use egui_winit::EventResponse;
|
use egui_winit::EventResponse;
|
||||||
|
@ -19,12 +19,14 @@ use euclid::{Box2D, Length, Point2D, Scale, Size2D};
|
||||||
use gleam::gl;
|
use gleam::gl;
|
||||||
use glow::NativeFramebuffer;
|
use glow::NativeFramebuffer;
|
||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
|
use servo::base::id::WebViewId;
|
||||||
use servo::compositing::windowing::EmbedderEvent;
|
use servo::compositing::windowing::EmbedderEvent;
|
||||||
use servo::script_traits::TraversalDirection;
|
use servo::script_traits::TraversalDirection;
|
||||||
use servo::servo_geometry::DeviceIndependentPixel;
|
use servo::servo_geometry::DeviceIndependentPixel;
|
||||||
use servo::servo_url::ServoUrl;
|
use servo::servo_url::ServoUrl;
|
||||||
use servo::style_traits::DevicePixel;
|
use servo::style_traits::DevicePixel;
|
||||||
use servo::webrender_traits::RenderingContext;
|
use servo::webrender_traits::RenderingContext;
|
||||||
|
use servo::TopLevelBrowsingContextId;
|
||||||
use winit::event::{ElementState, MouseButton};
|
use winit::event::{ElementState, MouseButton};
|
||||||
|
|
||||||
use super::egui_glue::EguiGlow;
|
use super::egui_glue::EguiGlow;
|
||||||
|
@ -61,6 +63,7 @@ pub enum MinibrowserEvent {
|
||||||
Back,
|
Back,
|
||||||
Forward,
|
Forward,
|
||||||
Reload,
|
Reload,
|
||||||
|
NewWebView,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn truncate_with_ellipsis(input: &str, max_length: usize) -> String {
|
fn truncate_with_ellipsis(input: &str, max_length: usize) -> String {
|
||||||
|
@ -163,6 +166,87 @@ impl Minibrowser {
|
||||||
.min_size(Vec2 { x: 20.0, y: 20.0 })
|
.min_size(Vec2 { x: 20.0, y: 20.0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draws a browser tab, checking for clicks and returns an appropriate [EmbedderEvent]
|
||||||
|
/// Using a custom widget here would've been nice, but it doesn't seem as though egui
|
||||||
|
/// supports that, so we arrange multiple Widgets in a way that they look connected.
|
||||||
|
fn browser_tab(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
label: &str,
|
||||||
|
selected: bool,
|
||||||
|
webview_id: TopLevelBrowsingContextId,
|
||||||
|
) -> Option<EmbedderEvent> {
|
||||||
|
let old_item_spacing = ui.spacing().item_spacing;
|
||||||
|
let old_visuals = ui.visuals().clone();
|
||||||
|
let active_bg_color = old_visuals.widgets.active.weak_bg_fill;
|
||||||
|
let inactive_bg_color = old_visuals.window_fill;
|
||||||
|
ui.spacing_mut().item_spacing = egui::vec2(0.0, 0.0);
|
||||||
|
|
||||||
|
let visuals = ui.visuals_mut();
|
||||||
|
// Remove the stroke so we don't see the border between the close button and the label
|
||||||
|
visuals.widgets.active.bg_stroke.width = 0.0;
|
||||||
|
visuals.widgets.hovered.bg_stroke.width = 0.0;
|
||||||
|
// Now we make sure the fill color is always the same, irrespective of state, that way
|
||||||
|
// we can make sure that both the label and close button have the same background color
|
||||||
|
visuals.widgets.noninteractive.weak_bg_fill = inactive_bg_color;
|
||||||
|
visuals.widgets.inactive.weak_bg_fill = inactive_bg_color;
|
||||||
|
visuals.widgets.hovered.weak_bg_fill = active_bg_color;
|
||||||
|
visuals.widgets.active.weak_bg_fill = active_bg_color;
|
||||||
|
visuals.selection.bg_fill = active_bg_color;
|
||||||
|
visuals.selection.stroke.color = visuals.widgets.active.fg_stroke.color;
|
||||||
|
visuals.widgets.hovered.fg_stroke.color = visuals.widgets.active.fg_stroke.color;
|
||||||
|
|
||||||
|
// Expansion would also show that they are 2 separate widgets
|
||||||
|
visuals.widgets.active.expansion = 0.0;
|
||||||
|
visuals.widgets.hovered.expansion = 0.0;
|
||||||
|
// The rounding is changed so it looks as though the 2 widgets are a single widget
|
||||||
|
// with a uniform rounding
|
||||||
|
let rounding = egui::Rounding {
|
||||||
|
ne: 0.0,
|
||||||
|
nw: 4.0,
|
||||||
|
sw: 4.0,
|
||||||
|
se: 0.0,
|
||||||
|
};
|
||||||
|
visuals.widgets.active.rounding = rounding;
|
||||||
|
visuals.widgets.hovered.rounding = rounding;
|
||||||
|
visuals.widgets.inactive.rounding = rounding;
|
||||||
|
|
||||||
|
let tab = ui.add(SelectableLabel::new(
|
||||||
|
selected,
|
||||||
|
truncate_with_ellipsis(label, 20),
|
||||||
|
));
|
||||||
|
let tab = tab.on_hover_ui(|ui| {
|
||||||
|
ui.label(label);
|
||||||
|
});
|
||||||
|
|
||||||
|
let rounding = egui::Rounding {
|
||||||
|
ne: 4.0,
|
||||||
|
nw: 0.0,
|
||||||
|
sw: 0.0,
|
||||||
|
se: 4.0,
|
||||||
|
};
|
||||||
|
let visuals = ui.visuals_mut();
|
||||||
|
visuals.widgets.active.rounding = rounding;
|
||||||
|
visuals.widgets.hovered.rounding = rounding;
|
||||||
|
visuals.widgets.inactive.rounding = rounding;
|
||||||
|
|
||||||
|
let fill_color = if selected || tab.hovered() {
|
||||||
|
active_bg_color
|
||||||
|
} else {
|
||||||
|
inactive_bg_color
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.spacing_mut().item_spacing = old_item_spacing;
|
||||||
|
let close_button = ui.add(egui::Button::new("X").fill(fill_color));
|
||||||
|
*ui.visuals_mut() = old_visuals;
|
||||||
|
if close_button.clicked() || close_button.middle_clicked() || tab.middle_clicked() {
|
||||||
|
Some(EmbedderEvent::CloseWebView(webview_id))
|
||||||
|
} else if !selected && tab.clicked() {
|
||||||
|
Some(EmbedderEvent::FocusWebView(webview_id))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the minibrowser, but don’t paint.
|
/// Update the minibrowser, but don’t paint.
|
||||||
/// If `servo_framebuffer_id` is given, set up a paint callback to blit its contents to our
|
/// If `servo_framebuffer_id` is given, set up a paint callback to blit its contents to our
|
||||||
/// CentralPanel when [`Minibrowser::paint`] is called.
|
/// CentralPanel when [`Minibrowser::paint`] is called.
|
||||||
|
@ -195,7 +279,7 @@ impl Minibrowser {
|
||||||
// when not displaying the URL bar: https://github.com/servo/servo/issues/32443
|
// when not displaying the URL bar: https://github.com/servo/servo/issues/32443
|
||||||
if window.fullscreen().is_none() {
|
if window.fullscreen().is_none() {
|
||||||
let frame = egui::Frame::default()
|
let frame = egui::Frame::default()
|
||||||
.fill(Color32::from_gray(32))
|
.fill(ctx.style().visuals.window_fill)
|
||||||
.inner_margin(4.0);
|
.inner_margin(4.0);
|
||||||
TopBottomPanel::top("toolbar").frame(frame).show(ctx, |ui| {
|
TopBottomPanel::top("toolbar").frame(frame).show(ctx, |ui| {
|
||||||
ui.allocate_ui_with_layout(
|
ui.allocate_ui_with_layout(
|
||||||
|
@ -267,30 +351,27 @@ impl Minibrowser {
|
||||||
|
|
||||||
let mut embedder_events = vec![];
|
let mut embedder_events = vec![];
|
||||||
|
|
||||||
// A simple Tab header strip, using egui 'SelectableLabel' elements.
|
// A simple Tab header strip
|
||||||
// TODO: Add a way to close a tab eg. with a [x] control.
|
|
||||||
TopBottomPanel::top("tabs").show(ctx, |ui| {
|
TopBottomPanel::top("tabs").show(ctx, |ui| {
|
||||||
ui.allocate_ui_with_layout(
|
ui.allocate_ui_with_layout(
|
||||||
ui.available_size(),
|
ui.available_size(),
|
||||||
egui::Layout::left_to_right(egui::Align::Center),
|
egui::Layout::left_to_right(egui::Align::Center),
|
||||||
|ui| {
|
|ui| {
|
||||||
for (webview_id, webview) in webviews.webviews().into_iter() {
|
for (webview_id, webview) in webviews.webviews().into_iter() {
|
||||||
let msg = match (&webview.title, &webview.url) {
|
let label = match (&webview.title, &webview.url) {
|
||||||
(Some(title), _) if !title.is_empty() => title.clone(),
|
(Some(title), _) => title,
|
||||||
(_, Some(url)) => url.to_string(),
|
(None, Some(url)) => &url.to_string(),
|
||||||
_ => "New Tab".to_owned(),
|
_ => "New Tab",
|
||||||
};
|
};
|
||||||
let tab = ui.add(SelectableLabel::new(
|
if let Some(event) =
|
||||||
webview.focused,
|
Self::browser_tab(ui, label, webview.focused, webview_id)
|
||||||
truncate_with_ellipsis(&msg, 20),
|
{
|
||||||
));
|
embedder_events.push(event);
|
||||||
let tab = tab.on_hover_ui(|ui| {
|
|
||||||
ui.label(&msg);
|
|
||||||
});
|
|
||||||
if !webview.focused && tab.clicked() {
|
|
||||||
embedder_events.push(EmbedderEvent::FocusWebView(webview_id));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ui.add(Minibrowser::toolbar_button("+")).clicked() {
|
||||||
|
event_queue.borrow_mut().push(MinibrowserEvent::NewWebView);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -441,6 +522,10 @@ impl Minibrowser {
|
||||||
let browser_id = browser.focused_webview_id().unwrap();
|
let browser_id = browser.focused_webview_id().unwrap();
|
||||||
app_event_queue.push(EmbedderEvent::Reload(browser_id));
|
app_event_queue.push(EmbedderEvent::Reload(browser_id));
|
||||||
},
|
},
|
||||||
|
MinibrowserEvent::NewWebView => {
|
||||||
|
let url = ServoUrl::parse("servo:newtab").unwrap();
|
||||||
|
app_event_queue.push(EmbedderEvent::NewWebView(url, WebViewId::new()));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue