versoview/webview/
history_menu.rsuse super::webview_menu::WebViewMenu;
use crate::webview::WebView;
use crate::{verso::send_to_constellation, window::Window};
use base::id::WebViewId;
use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection};
use crossbeam_channel::Sender;
use embedder_traits::ViewportDetails;
use serde::{Deserialize, Serialize};
use servo_url::ServoUrl;
use std::fmt;
use url::Url;
use webrender_api::units::DeviceRect;
use winit::dpi::LogicalPosition;
#[derive(Clone)]
pub struct HistoryMenu {
action: HistoryMenuAction,
menu_items: Vec<HistoryMenuItem>,
pub(crate) webview: WebView,
position: LogicalPosition<f64>,
}
impl HistoryMenu {
pub fn new_with_menu(action: HistoryMenuAction, menu_items: Vec<HistoryMenuItem>) -> Self {
let webview_id = WebViewId::new();
let webview = WebView::new(webview_id, ViewportDetails::default());
Self {
action,
menu_items,
webview,
position: LogicalPosition::new(0.0, 0.0),
}
}
}
impl WebViewMenu for HistoryMenu {
fn webview(&self) -> &WebView {
&self.webview
}
fn resource_url(&self) -> ServoUrl {
let mut url = Url::parse("verso://resources/components/history_menu.html").unwrap();
url.query_pairs_mut()
.append_pair("action", &self.action.to_string());
url.query_pairs_mut()
.append_pair("items", &self.serialize_items());
url.query_pairs_mut()
.append_pair("pos_x", &self.position.x.to_string());
url.query_pairs_mut()
.append_pair("pos_y", &self.position.y.to_string());
ServoUrl::from_url(url)
}
fn set_webview_rect(&mut self, rect: DeviceRect) {
self.webview.set_size(rect);
}
fn position(&self) -> LogicalPosition<f64> {
self.position
}
fn set_position(&mut self, position: LogicalPosition<f64>) {
self.position = position;
}
fn close(&mut self, sender: &Sender<EmbedderToConstellationMessage>) {
send_to_constellation(
sender,
EmbedderToConstellationMessage::CloseWebView(self.webview().webview_id),
);
}
}
impl HistoryMenu {
fn serialize_items(&self) -> String {
serde_json::to_string(&self.menu_items).unwrap()
}
}
#[derive(Debug, Clone, Serialize)]
pub struct HistoryMenuItem {
pub index: usize,
pub title: String,
pub url: String,
}
impl HistoryMenuItem {
pub fn new(index: usize, title: Option<&str>, url: &str) -> Self {
let title = title.unwrap_or(url);
Self {
index,
title: title.to_string(),
url: url.to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoryMenuUIResponse {
pub index: Option<usize>,
pub action: HistoryMenuAction,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenHistoryMenuRequest {
pub action: HistoryMenuAction,
pub position: LogicalPosition<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum HistoryMenuAction {
Prev,
Next,
}
impl fmt::Display for HistoryMenuAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HistoryMenuAction::Prev => write!(f, "Prev"),
HistoryMenuAction::Next => write!(f, "Next"),
}
}
}
impl Window {
pub(crate) fn show_history_menu(
&mut self,
sender: &Sender<EmbedderToConstellationMessage>,
request: OpenHistoryMenuRequest,
) -> Option<HistoryMenu> {
let tab = self.tab_manager.current_tab().unwrap();
let history = tab.history();
let current_index = history.current_idx;
let items: Vec<HistoryMenuItem> = match request.action {
HistoryMenuAction::Prev => {
history.list[..current_index]
.iter()
.rev()
.enumerate()
.map(|(index, url)| HistoryMenuItem::new(index, None, url.as_str()))
.collect()
}
HistoryMenuAction::Next => {
history.list[current_index + 1..]
.iter()
.enumerate()
.map(|(index, url)| HistoryMenuItem::new(index, None, url.as_str()))
.collect()
}
};
if items.is_empty() {
return None;
}
let mut menu = HistoryMenu::new_with_menu(request.action, items);
menu.show(
sender,
self,
request.position.to_physical(self.scale_factor()),
);
Some(menu)
}
pub(crate) fn handle_history_menu_event(
&mut self,
sender: &Sender<EmbedderToConstellationMessage>,
event: crate::webview::history_menu::HistoryMenuUIResponse,
) {
self.close_webview_menu(sender);
if let Some(tab_id) = self.tab_manager.current_tab_id() {
if let Some(index) = event.index {
match event.action {
HistoryMenuAction::Prev => {
send_to_constellation(
sender,
EmbedderToConstellationMessage::TraverseHistory(
tab_id,
TraversalDirection::Back(index + 1),
),
);
}
HistoryMenuAction::Next => {
send_to_constellation(
sender,
EmbedderToConstellationMessage::TraverseHistory(
tab_id,
TraversalDirection::Forward(index + 1),
),
);
}
}
}
} else {
log::error!("No active webview to handle history menu event");
}
}
}