Add Servo page zoom support

This commit is contained in:
2026-05-09 12:32:44 -04:00
parent 088d1166c4
commit 1faa7423c1
26 changed files with 697 additions and 280 deletions
@@ -133,6 +133,10 @@ impl BrowserCore {
self.archive_idle_tabs(SystemTime::now())?;
return Ok(true);
}
if let Some(percent) = zoom_percent(command)? {
self.set_active_tab_zoom_percent(percent)?;
return Ok(true);
}
if let Some(percent) = reading_progress_percent(command)? {
self.set_active_tab_reading_progress(percent)?;
return Ok(true);
@@ -167,6 +171,18 @@ impl BrowserCore {
self.open_new_tab()?;
Ok(true)
}
"zoom-in" | "zoom in" => {
self.zoom_active_tab_in()?;
Ok(true)
}
"zoom-out" | "zoom out" => {
self.zoom_active_tab_out()?;
Ok(true)
}
"reset-zoom" | "reset zoom" | "actual-size" | "actual size" => {
self.reset_active_tab_zoom()?;
Ok(true)
}
"move-tab-up" | "move tab up" | "tab-up" | "tab up" => self.move_active_tab_up(),
"move-tab-down" | "move tab down" | "tab-down" | "tab down" => {
self.move_active_tab_down()
@@ -446,3 +462,19 @@ impl BrowserCore {
.transpose()
}
}
fn zoom_percent(command: &str) -> Result<Option<u16>, CoreError> {
let command = command.trim();
let lowercased = command.to_ascii_lowercase();
let value = ["zoom ", "set-zoom ", "set zoom "]
.into_iter()
.find_map(|prefix| lowercased.starts_with(prefix).then(|| &command[prefix.len()..]));
let Some(value) = value else { return Ok(None) };
let percent = value
.trim()
.trim_end_matches('%')
.parse::<u16>()
.map_err(|_| CoreError::Domain(ely_domain::DomainError::InvalidCommand))?;
Ok(Some(ely_domain::validate_zoom_percent(percent)?))
}
+29
View File
@@ -220,6 +220,30 @@ impl BrowserCore {
Ok(next_pinned)
}
pub fn set_active_tab_zoom_percent(&mut self, zoom_percent: u16) -> Result<u16, CoreError> {
let active_tab = self.active_tab_mut()?;
active_tab.set_zoom_percent(zoom_percent)?;
Ok(active_tab.zoom_percent())
}
pub fn zoom_active_tab_in(&mut self) -> Result<u16, CoreError> {
let active_tab = self.active_tab_mut()?;
active_tab.zoom_in();
Ok(active_tab.zoom_percent())
}
pub fn zoom_active_tab_out(&mut self) -> Result<u16, CoreError> {
let active_tab = self.active_tab_mut()?;
active_tab.zoom_out();
Ok(active_tab.zoom_percent())
}
pub fn reset_active_tab_zoom(&mut self) -> Result<u16, CoreError> {
let active_tab = self.active_tab_mut()?;
active_tab.reset_zoom();
Ok(active_tab.zoom_percent())
}
pub fn set_tab_sync_enabled(
&mut self,
tab_id: &TabId,
@@ -337,6 +361,11 @@ impl BrowserCore {
.ok_or(CoreError::MissingActiveTab)
}
fn active_tab_mut(&mut self) -> Result<&mut BrowserTab, CoreError> {
let active_index = self.active_tab_index()?;
self.tabs.get_mut(active_index).ok_or(CoreError::MissingActiveTab)
}
pub(super) fn build_tab(&self, url: UrlText) -> BrowserTab {
self.build_tab_for(self.active_space_id.clone(), self.active_profile_id.clone(), url)
.with_parent_tab_id(self.active_tab_id.clone())
+69 -1
View File
@@ -1,7 +1,10 @@
use std::error::Error;
use ely_browser_core::{BrowserCore, CoreError, InitialBrowserConfig};
use ely_domain::{CommandIntent, CommandScope, NewTabDestination, SearchEngine, TabState, UrlText};
use ely_domain::{
CommandIntent, CommandScope, DEFAULT_ZOOM_PERCENT, DomainError, MAX_ZOOM_PERCENT,
MIN_ZOOM_PERCENT, NewTabDestination, SearchEngine, TabState, UrlText,
};
#[test]
fn opens_new_tab_below_active_tab() -> Result<(), Box<dyn Error>> {
@@ -357,6 +360,71 @@ fn new_tab_command_uses_selected_destination() -> Result<(), Box<dyn Error>> {
Ok(())
}
#[test]
fn active_tab_zoom_updates_tab_state() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
assert_eq!(core.active_tab()?.zoom_percent(), DEFAULT_ZOOM_PERCENT);
assert_eq!(core.zoom_active_tab_in()?, DEFAULT_ZOOM_PERCENT + 10);
assert_eq!(core.zoom_active_tab_out()?, DEFAULT_ZOOM_PERCENT);
assert_eq!(core.set_active_tab_zoom_percent(125)?, 125);
assert_eq!(core.active_tab()?.zoom_factor(), 1.25);
assert_eq!(core.reset_active_tab_zoom()?, DEFAULT_ZOOM_PERCENT);
Ok(())
}
#[test]
fn active_tab_zoom_clamps_incremental_commands() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
core.set_active_tab_zoom_percent(MAX_ZOOM_PERCENT)?;
assert_eq!(core.zoom_active_tab_in()?, MAX_ZOOM_PERCENT);
core.set_active_tab_zoom_percent(MIN_ZOOM_PERCENT)?;
assert_eq!(core.zoom_active_tab_out()?, MIN_ZOOM_PERCENT);
Ok(())
}
#[test]
fn active_tab_zoom_rejects_out_of_range_percent() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
let error = match core.set_active_tab_zoom_percent(5) {
Err(error) => error,
Ok(_) => return Err("zoom should require an in-range percent".into()),
};
assert_eq!(
error,
CoreError::Domain(DomainError::InvalidZoomPercent {
value: 5,
min: MIN_ZOOM_PERCENT,
max: MAX_ZOOM_PERCENT,
})
);
Ok(())
}
#[test]
fn zoom_commands_update_active_tab() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
core.set_command_query(">zoom-in");
let intent = core.submit_command()?;
assert_eq!(intent, Some(CommandIntent::Command("zoom-in".to_string())));
assert_eq!(core.active_tab()?.zoom_percent(), DEFAULT_ZOOM_PERCENT + 10);
core.set_command_query(">zoom 125%");
core.submit_command()?;
assert_eq!(core.active_tab()?.zoom_percent(), 125);
core.set_command_query(">actual-size");
core.submit_command()?;
assert_eq!(core.active_tab()?.zoom_percent(), DEFAULT_ZOOM_PERCENT);
assert_eq!(core.command_query(), "");
Ok(())
}
#[test]
fn switching_spaces_restores_each_space_active_tab() -> Result<(), Box<dyn Error>> {
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;