Add private window entry point
This commit is contained in:
+55
-24
@@ -19,6 +19,7 @@ actions!(
|
||||
OpenDownloads,
|
||||
OpenHistory,
|
||||
OpenNewTab,
|
||||
OpenPrivateWindow,
|
||||
OpenSettings,
|
||||
OpenTaskManager,
|
||||
Quit,
|
||||
@@ -39,6 +40,7 @@ fn main() {
|
||||
gpui_component::init(cx);
|
||||
cx.on_action(quit);
|
||||
bind_shortcuts(cx);
|
||||
cx.on_action(open_private_window);
|
||||
cx.set_menus(vec![
|
||||
Menu {
|
||||
name: "ELY Browser".into(),
|
||||
@@ -52,6 +54,7 @@ fn main() {
|
||||
name: "File".into(),
|
||||
items: vec![
|
||||
MenuItem::action("New Tab", OpenNewTab),
|
||||
MenuItem::action("New Private Window", OpenPrivateWindow),
|
||||
MenuItem::action("Split Right", SplitRight),
|
||||
MenuItem::separator(),
|
||||
MenuItem::action("Command Mode", FocusCommandMode),
|
||||
@@ -78,30 +81,7 @@ fn main() {
|
||||
},
|
||||
]);
|
||||
|
||||
let bounds = Bounds::centered(None, size(px(1240.0), px(780.0)), cx);
|
||||
let opened = cx.open_window(
|
||||
WindowOptions {
|
||||
titlebar: Some(TitlebarOptions {
|
||||
title: Some("ELY Browser".into()),
|
||||
appears_transparent: true,
|
||||
traffic_light_position: Some(point(px(18.0), px(24.0))),
|
||||
}),
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..WindowOptions::default()
|
||||
},
|
||||
|window, cx| {
|
||||
let shell = cx.new(|cx| ElyShell::new(window, cx));
|
||||
let focus_handle = shell.focus_handle(cx);
|
||||
window.defer(cx, move |window, cx| {
|
||||
if window.focused(cx).is_none() {
|
||||
focus_handle.focus(window);
|
||||
}
|
||||
});
|
||||
cx.new(|cx| gpui_component::Root::new(shell, window, cx))
|
||||
},
|
||||
);
|
||||
|
||||
if opened.is_ok() {
|
||||
if open_browser_window(cx, BrowserWindowMode::Standard) {
|
||||
cx.activate(true);
|
||||
} else {
|
||||
cx.quit();
|
||||
@@ -109,6 +89,57 @@ fn main() {
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum BrowserWindowMode {
|
||||
Standard,
|
||||
Private,
|
||||
}
|
||||
|
||||
impl BrowserWindowMode {
|
||||
fn title(self) -> &'static str {
|
||||
match self {
|
||||
Self::Standard => "ELY Browser",
|
||||
Self::Private => "ELY Browser - Private",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_browser_window(cx: &mut App, mode: BrowserWindowMode) -> bool {
|
||||
let bounds = Bounds::centered(None, size(px(1240.0), px(780.0)), cx);
|
||||
let opened = cx.open_window(
|
||||
WindowOptions {
|
||||
titlebar: Some(TitlebarOptions {
|
||||
title: Some(mode.title().into()),
|
||||
appears_transparent: true,
|
||||
traffic_light_position: Some(point(px(18.0), px(24.0))),
|
||||
}),
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
..WindowOptions::default()
|
||||
},
|
||||
|window, cx| {
|
||||
let shell = cx.new(|cx| match mode {
|
||||
BrowserWindowMode::Standard => ElyShell::new(window, cx),
|
||||
BrowserWindowMode::Private => ElyShell::new_private(window, cx),
|
||||
});
|
||||
let focus_handle = shell.focus_handle(cx);
|
||||
window.defer(cx, move |window, cx| {
|
||||
if window.focused(cx).is_none() {
|
||||
focus_handle.focus(window);
|
||||
}
|
||||
});
|
||||
cx.new(|cx| gpui_component::Root::new(shell, window, cx))
|
||||
},
|
||||
);
|
||||
|
||||
opened.is_ok()
|
||||
}
|
||||
|
||||
fn quit(_: &Quit, cx: &mut App) {
|
||||
cx.quit();
|
||||
}
|
||||
|
||||
fn open_private_window(_: &OpenPrivateWindow, cx: &mut App) {
|
||||
if open_browser_window(cx, BrowserWindowMode::Private) {
|
||||
cx.activate(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,18 @@ pub struct ElyShell {
|
||||
|
||||
impl ElyShell {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
Self::new_with_config(InitialBrowserConfig::ely_defaults(), window, cx)
|
||||
}
|
||||
|
||||
pub fn new_private(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
Self::new_with_config(InitialBrowserConfig::private_window(), window, cx)
|
||||
}
|
||||
|
||||
fn new_with_config(
|
||||
config: Result<InitialBrowserConfig, ely_domain::DomainError>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let command_input =
|
||||
cx.new(|cx| InputState::new(window, cx).placeholder("Search or enter address"));
|
||||
|
||||
@@ -112,7 +124,7 @@ impl ElyShell {
|
||||
},
|
||||
);
|
||||
|
||||
let state = match InitialBrowserConfig::ely_defaults().and_then(|config| {
|
||||
let state = match config.and_then(|config| {
|
||||
BrowserCore::new(config).map_err(|error| match error {
|
||||
ely_browser_core::CoreError::Domain(source) => source,
|
||||
_ => ely_domain::DomainError::InvalidCommand,
|
||||
|
||||
@@ -74,7 +74,10 @@ mod tests {
|
||||
let mut core = BrowserCore::new(InitialBrowserConfig {
|
||||
space_name: "Work".to_string(),
|
||||
space_icon: "W".to_string(),
|
||||
space_color_hex: 0xf54e00,
|
||||
profile_name: "Default".to_string(),
|
||||
profile_color_hex: 0x26251e,
|
||||
profile_kind: ely_domain::ProfileKind::Standard,
|
||||
new_tab_destination: Default::default(),
|
||||
})?;
|
||||
core.open_tab(UrlText::parse(url)?);
|
||||
|
||||
@@ -4,8 +4,9 @@ use gpui::{App, KeyBinding};
|
||||
|
||||
use crate::{
|
||||
CloseCurrentTab, FocusAddressBar, FocusCommandMode, OpenDownloads, OpenHistory, OpenNewTab,
|
||||
OpenSettings, OpenTaskManager, Quit, RestoreClosedTab, SelectNextSpace, SelectNextTab,
|
||||
SelectPreviousSpace, SelectPreviousTab, SplitRight, ToggleFavoriteTab, ToggleSidebar,
|
||||
OpenPrivateWindow, OpenSettings, OpenTaskManager, Quit, RestoreClosedTab, SelectNextSpace,
|
||||
SelectNextTab, SelectPreviousSpace, SelectPreviousTab, SplitRight, ToggleFavoriteTab,
|
||||
ToggleSidebar,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
@@ -28,6 +29,7 @@ pub(crate) enum ShortcutAction {
|
||||
FocusAddressBar,
|
||||
FocusCommandMode,
|
||||
OpenNewTab,
|
||||
OpenPrivateWindow,
|
||||
CloseCurrentTab,
|
||||
RestoreClosedTab,
|
||||
SelectNextSpace,
|
||||
@@ -50,6 +52,7 @@ impl ShortcutAction {
|
||||
Self::FocusAddressBar => "Command Bar",
|
||||
Self::FocusCommandMode => "Command Mode",
|
||||
Self::OpenNewTab => "New Tab",
|
||||
Self::OpenPrivateWindow => "New Private Window",
|
||||
Self::CloseCurrentTab => "Close Tab",
|
||||
Self::RestoreClosedTab => "Restore Closed Tab",
|
||||
Self::SelectNextSpace => "Next Space",
|
||||
@@ -71,6 +74,7 @@ impl ShortcutAction {
|
||||
match self {
|
||||
Self::FocusAddressBar | Self::FocusCommandMode => "Command",
|
||||
Self::OpenNewTab
|
||||
| Self::OpenPrivateWindow
|
||||
| Self::CloseCurrentTab
|
||||
| Self::RestoreClosedTab
|
||||
| Self::SelectNextSpace
|
||||
@@ -91,6 +95,7 @@ impl ShortcutAction {
|
||||
Self::FocusAddressBar => None,
|
||||
Self::FocusCommandMode => None,
|
||||
Self::OpenNewTab => Some(">new-tab"),
|
||||
Self::OpenPrivateWindow => None,
|
||||
Self::CloseCurrentTab => Some(">close-tab"),
|
||||
Self::RestoreClosedTab => Some(">restore-tab"),
|
||||
Self::SelectNextSpace => None,
|
||||
@@ -133,6 +138,7 @@ pub(crate) const SHORTCUT_ACTIONS: &[ShortcutAction] = &[
|
||||
ShortcutAction::FocusAddressBar,
|
||||
ShortcutAction::FocusCommandMode,
|
||||
ShortcutAction::OpenNewTab,
|
||||
ShortcutAction::OpenPrivateWindow,
|
||||
ShortcutAction::CloseCurrentTab,
|
||||
ShortcutAction::RestoreClosedTab,
|
||||
ShortcutAction::SelectNextSpace,
|
||||
@@ -152,6 +158,8 @@ pub(crate) const SHORTCUT_ACTIONS: &[ShortcutAction] = &[
|
||||
pub(crate) const SHORTCUT_BINDINGS: &[ShortcutBinding] = &[
|
||||
shortcut(ShortcutAction::OpenNewTab, ShortcutPlatform::Macos, "cmd-t"),
|
||||
shortcut(ShortcutAction::OpenNewTab, ShortcutPlatform::WindowsLinux, "ctrl-t"),
|
||||
shortcut(ShortcutAction::OpenPrivateWindow, ShortcutPlatform::Macos, "cmd-shift-n"),
|
||||
shortcut(ShortcutAction::OpenPrivateWindow, ShortcutPlatform::WindowsLinux, "ctrl-shift-n"),
|
||||
shortcut(ShortcutAction::SplitRight, ShortcutPlatform::Macos, "cmd-\\"),
|
||||
shortcut(ShortcutAction::SplitRight, ShortcutPlatform::WindowsLinux, "ctrl-\\"),
|
||||
shortcut(ShortcutAction::ToggleSidebar, ShortcutPlatform::Macos, "cmd-b"),
|
||||
@@ -245,6 +253,9 @@ impl ShortcutBinding {
|
||||
ShortcutAction::OpenDownloads => KeyBinding::new(self.keystroke, OpenDownloads, None),
|
||||
ShortcutAction::OpenHistory => KeyBinding::new(self.keystroke, OpenHistory, None),
|
||||
ShortcutAction::OpenNewTab => KeyBinding::new(self.keystroke, OpenNewTab, None),
|
||||
ShortcutAction::OpenPrivateWindow => {
|
||||
KeyBinding::new(self.keystroke, OpenPrivateWindow, None)
|
||||
}
|
||||
ShortcutAction::OpenSettings => KeyBinding::new(self.keystroke, OpenSettings, None),
|
||||
ShortcutAction::OpenTaskManager => {
|
||||
KeyBinding::new(self.keystroke, OpenTaskManager, None)
|
||||
@@ -317,6 +328,20 @@ mod tests {
|
||||
assert_eq!(bindings, vec!["Cmd + ,".to_string(), "Ctrl + ,".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_window_shortcut_has_platform_bindings() {
|
||||
let bindings =
|
||||
bindings_for_action(ShortcutAction::OpenPrivateWindow, ShortcutPlatform::Macos)
|
||||
.chain(bindings_for_action(
|
||||
ShortcutAction::OpenPrivateWindow,
|
||||
ShortcutPlatform::WindowsLinux,
|
||||
))
|
||||
.map(|binding| binding.display_keystroke())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(bindings, vec!["Cmd + Shift + N".to_string(), "Ctrl + Shift + N".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toggle_sidebar_shortcut_has_platform_bindings() {
|
||||
let bindings = bindings_for_action(ShortcutAction::ToggleSidebar, ShortcutPlatform::Macos)
|
||||
|
||||
@@ -43,7 +43,10 @@ pub use spaces::TrashedSpace;
|
||||
pub struct InitialBrowserConfig {
|
||||
pub space_name: String,
|
||||
pub space_icon: String,
|
||||
pub space_color_hex: u32,
|
||||
pub profile_name: String,
|
||||
pub profile_color_hex: u32,
|
||||
pub profile_kind: ProfileKind,
|
||||
pub new_tab_destination: NewTabDestination,
|
||||
}
|
||||
|
||||
@@ -52,7 +55,22 @@ impl InitialBrowserConfig {
|
||||
Ok(Self {
|
||||
space_name: "Work".to_string(),
|
||||
space_icon: "W".to_string(),
|
||||
space_color_hex: 0xf54e00,
|
||||
profile_name: "Default".to_string(),
|
||||
profile_color_hex: 0x26251e,
|
||||
profile_kind: ProfileKind::Standard,
|
||||
new_tab_destination: NewTabDestination::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn private_window() -> Result<Self, DomainError> {
|
||||
Ok(Self {
|
||||
space_name: "Private".to_string(),
|
||||
space_icon: "P".to_string(),
|
||||
space_color_hex: 0x807d72,
|
||||
profile_name: "Private".to_string(),
|
||||
profile_color_hex: 0x807d72,
|
||||
profile_kind: ProfileKind::Private,
|
||||
new_tab_destination: NewTabDestination::default(),
|
||||
})
|
||||
}
|
||||
@@ -127,12 +145,13 @@ pub struct BrowserCore {
|
||||
|
||||
impl BrowserCore {
|
||||
pub fn new(config: InitialBrowserConfig) -> Result<Self, CoreError> {
|
||||
let profile = Profile::new(config.profile_name, 0x26251e, ProfileKind::Standard);
|
||||
let profile =
|
||||
Profile::new(config.profile_name, config.profile_color_hex, config.profile_kind);
|
||||
let active_profile_id = profile.id().clone();
|
||||
let space = Space::new(
|
||||
config.space_name,
|
||||
config.space_icon,
|
||||
0xf54e00,
|
||||
config.space_color_hex,
|
||||
active_profile_id.clone(),
|
||||
0,
|
||||
);
|
||||
|
||||
@@ -24,6 +24,26 @@ fn new_private_profile_command_creates_private_profile() -> Result<(), Box<dyn E
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_window_config_starts_with_private_profile() -> Result<(), Box<dyn Error>> {
|
||||
let mut core = BrowserCore::new(InitialBrowserConfig::private_window()?)?;
|
||||
let snapshot = core.snapshot()?;
|
||||
let Some(private_profile) =
|
||||
snapshot.profiles.iter().find(|profile| profile.id() == &snapshot.active_profile_id)
|
||||
else {
|
||||
return Err("missing active private profile".into());
|
||||
};
|
||||
|
||||
assert_eq!(snapshot.active_space_name, "Private");
|
||||
assert_eq!(snapshot.active_profile_name, "Private");
|
||||
assert_eq!(private_profile.kind(), &ProfileKind::Private);
|
||||
assert_eq!(private_profile.sync_policy(), ProfileSyncPolicy::Paused);
|
||||
|
||||
core.open_tab(UrlText::parse("https://example.com/private-window")?);
|
||||
assert!(core.snapshot()?.history_entries.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_profiles_do_not_record_history() -> Result<(), Box<dyn Error>> {
|
||||
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||
|
||||
Reference in New Issue
Block a user