Add private window entry point
This commit is contained in:
@@ -19,6 +19,7 @@ actions!(
|
|||||||
OpenDownloads,
|
OpenDownloads,
|
||||||
OpenHistory,
|
OpenHistory,
|
||||||
OpenNewTab,
|
OpenNewTab,
|
||||||
|
OpenPrivateWindow,
|
||||||
OpenSettings,
|
OpenSettings,
|
||||||
OpenTaskManager,
|
OpenTaskManager,
|
||||||
Quit,
|
Quit,
|
||||||
@@ -39,6 +40,7 @@ fn main() {
|
|||||||
gpui_component::init(cx);
|
gpui_component::init(cx);
|
||||||
cx.on_action(quit);
|
cx.on_action(quit);
|
||||||
bind_shortcuts(cx);
|
bind_shortcuts(cx);
|
||||||
|
cx.on_action(open_private_window);
|
||||||
cx.set_menus(vec![
|
cx.set_menus(vec![
|
||||||
Menu {
|
Menu {
|
||||||
name: "ELY Browser".into(),
|
name: "ELY Browser".into(),
|
||||||
@@ -52,6 +54,7 @@ fn main() {
|
|||||||
name: "File".into(),
|
name: "File".into(),
|
||||||
items: vec![
|
items: vec![
|
||||||
MenuItem::action("New Tab", OpenNewTab),
|
MenuItem::action("New Tab", OpenNewTab),
|
||||||
|
MenuItem::action("New Private Window", OpenPrivateWindow),
|
||||||
MenuItem::action("Split Right", SplitRight),
|
MenuItem::action("Split Right", SplitRight),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Command Mode", FocusCommandMode),
|
MenuItem::action("Command Mode", FocusCommandMode),
|
||||||
@@ -78,11 +81,35 @@ fn main() {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if open_browser_window(cx, BrowserWindowMode::Standard) {
|
||||||
|
cx.activate(true);
|
||||||
|
} else {
|
||||||
|
cx.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 bounds = Bounds::centered(None, size(px(1240.0), px(780.0)), cx);
|
||||||
let opened = cx.open_window(
|
let opened = cx.open_window(
|
||||||
WindowOptions {
|
WindowOptions {
|
||||||
titlebar: Some(TitlebarOptions {
|
titlebar: Some(TitlebarOptions {
|
||||||
title: Some("ELY Browser".into()),
|
title: Some(mode.title().into()),
|
||||||
appears_transparent: true,
|
appears_transparent: true,
|
||||||
traffic_light_position: Some(point(px(18.0), px(24.0))),
|
traffic_light_position: Some(point(px(18.0), px(24.0))),
|
||||||
}),
|
}),
|
||||||
@@ -90,7 +117,10 @@ fn main() {
|
|||||||
..WindowOptions::default()
|
..WindowOptions::default()
|
||||||
},
|
},
|
||||||
|window, cx| {
|
|window, cx| {
|
||||||
let shell = cx.new(|cx| ElyShell::new(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);
|
let focus_handle = shell.focus_handle(cx);
|
||||||
window.defer(cx, move |window, cx| {
|
window.defer(cx, move |window, cx| {
|
||||||
if window.focused(cx).is_none() {
|
if window.focused(cx).is_none() {
|
||||||
@@ -101,14 +131,15 @@ fn main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if opened.is_ok() {
|
opened.is_ok()
|
||||||
cx.activate(true);
|
|
||||||
} else {
|
|
||||||
cx.quit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quit(_: &Quit, cx: &mut App) {
|
fn quit(_: &Quit, cx: &mut App) {
|
||||||
cx.quit();
|
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 {
|
impl ElyShell {
|
||||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
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 =
|
let command_input =
|
||||||
cx.new(|cx| InputState::new(window, cx).placeholder("Search or enter address"));
|
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 {
|
BrowserCore::new(config).map_err(|error| match error {
|
||||||
ely_browser_core::CoreError::Domain(source) => source,
|
ely_browser_core::CoreError::Domain(source) => source,
|
||||||
_ => ely_domain::DomainError::InvalidCommand,
|
_ => ely_domain::DomainError::InvalidCommand,
|
||||||
|
|||||||
@@ -74,7 +74,10 @@ mod tests {
|
|||||||
let mut core = BrowserCore::new(InitialBrowserConfig {
|
let mut core = BrowserCore::new(InitialBrowserConfig {
|
||||||
space_name: "Work".to_string(),
|
space_name: "Work".to_string(),
|
||||||
space_icon: "W".to_string(),
|
space_icon: "W".to_string(),
|
||||||
|
space_color_hex: 0xf54e00,
|
||||||
profile_name: "Default".to_string(),
|
profile_name: "Default".to_string(),
|
||||||
|
profile_color_hex: 0x26251e,
|
||||||
|
profile_kind: ely_domain::ProfileKind::Standard,
|
||||||
new_tab_destination: Default::default(),
|
new_tab_destination: Default::default(),
|
||||||
})?;
|
})?;
|
||||||
core.open_tab(UrlText::parse(url)?);
|
core.open_tab(UrlText::parse(url)?);
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ use gpui::{App, KeyBinding};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CloseCurrentTab, FocusAddressBar, FocusCommandMode, OpenDownloads, OpenHistory, OpenNewTab,
|
CloseCurrentTab, FocusAddressBar, FocusCommandMode, OpenDownloads, OpenHistory, OpenNewTab,
|
||||||
OpenSettings, OpenTaskManager, Quit, RestoreClosedTab, SelectNextSpace, SelectNextTab,
|
OpenPrivateWindow, OpenSettings, OpenTaskManager, Quit, RestoreClosedTab, SelectNextSpace,
|
||||||
SelectPreviousSpace, SelectPreviousTab, SplitRight, ToggleFavoriteTab, ToggleSidebar,
|
SelectNextTab, SelectPreviousSpace, SelectPreviousTab, SplitRight, ToggleFavoriteTab,
|
||||||
|
ToggleSidebar,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
@@ -28,6 +29,7 @@ pub(crate) enum ShortcutAction {
|
|||||||
FocusAddressBar,
|
FocusAddressBar,
|
||||||
FocusCommandMode,
|
FocusCommandMode,
|
||||||
OpenNewTab,
|
OpenNewTab,
|
||||||
|
OpenPrivateWindow,
|
||||||
CloseCurrentTab,
|
CloseCurrentTab,
|
||||||
RestoreClosedTab,
|
RestoreClosedTab,
|
||||||
SelectNextSpace,
|
SelectNextSpace,
|
||||||
@@ -50,6 +52,7 @@ impl ShortcutAction {
|
|||||||
Self::FocusAddressBar => "Command Bar",
|
Self::FocusAddressBar => "Command Bar",
|
||||||
Self::FocusCommandMode => "Command Mode",
|
Self::FocusCommandMode => "Command Mode",
|
||||||
Self::OpenNewTab => "New Tab",
|
Self::OpenNewTab => "New Tab",
|
||||||
|
Self::OpenPrivateWindow => "New Private Window",
|
||||||
Self::CloseCurrentTab => "Close Tab",
|
Self::CloseCurrentTab => "Close Tab",
|
||||||
Self::RestoreClosedTab => "Restore Closed Tab",
|
Self::RestoreClosedTab => "Restore Closed Tab",
|
||||||
Self::SelectNextSpace => "Next Space",
|
Self::SelectNextSpace => "Next Space",
|
||||||
@@ -71,6 +74,7 @@ impl ShortcutAction {
|
|||||||
match self {
|
match self {
|
||||||
Self::FocusAddressBar | Self::FocusCommandMode => "Command",
|
Self::FocusAddressBar | Self::FocusCommandMode => "Command",
|
||||||
Self::OpenNewTab
|
Self::OpenNewTab
|
||||||
|
| Self::OpenPrivateWindow
|
||||||
| Self::CloseCurrentTab
|
| Self::CloseCurrentTab
|
||||||
| Self::RestoreClosedTab
|
| Self::RestoreClosedTab
|
||||||
| Self::SelectNextSpace
|
| Self::SelectNextSpace
|
||||||
@@ -91,6 +95,7 @@ impl ShortcutAction {
|
|||||||
Self::FocusAddressBar => None,
|
Self::FocusAddressBar => None,
|
||||||
Self::FocusCommandMode => None,
|
Self::FocusCommandMode => None,
|
||||||
Self::OpenNewTab => Some(">new-tab"),
|
Self::OpenNewTab => Some(">new-tab"),
|
||||||
|
Self::OpenPrivateWindow => None,
|
||||||
Self::CloseCurrentTab => Some(">close-tab"),
|
Self::CloseCurrentTab => Some(">close-tab"),
|
||||||
Self::RestoreClosedTab => Some(">restore-tab"),
|
Self::RestoreClosedTab => Some(">restore-tab"),
|
||||||
Self::SelectNextSpace => None,
|
Self::SelectNextSpace => None,
|
||||||
@@ -133,6 +138,7 @@ pub(crate) const SHORTCUT_ACTIONS: &[ShortcutAction] = &[
|
|||||||
ShortcutAction::FocusAddressBar,
|
ShortcutAction::FocusAddressBar,
|
||||||
ShortcutAction::FocusCommandMode,
|
ShortcutAction::FocusCommandMode,
|
||||||
ShortcutAction::OpenNewTab,
|
ShortcutAction::OpenNewTab,
|
||||||
|
ShortcutAction::OpenPrivateWindow,
|
||||||
ShortcutAction::CloseCurrentTab,
|
ShortcutAction::CloseCurrentTab,
|
||||||
ShortcutAction::RestoreClosedTab,
|
ShortcutAction::RestoreClosedTab,
|
||||||
ShortcutAction::SelectNextSpace,
|
ShortcutAction::SelectNextSpace,
|
||||||
@@ -152,6 +158,8 @@ pub(crate) const SHORTCUT_ACTIONS: &[ShortcutAction] = &[
|
|||||||
pub(crate) const SHORTCUT_BINDINGS: &[ShortcutBinding] = &[
|
pub(crate) const SHORTCUT_BINDINGS: &[ShortcutBinding] = &[
|
||||||
shortcut(ShortcutAction::OpenNewTab, ShortcutPlatform::Macos, "cmd-t"),
|
shortcut(ShortcutAction::OpenNewTab, ShortcutPlatform::Macos, "cmd-t"),
|
||||||
shortcut(ShortcutAction::OpenNewTab, ShortcutPlatform::WindowsLinux, "ctrl-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::Macos, "cmd-\\"),
|
||||||
shortcut(ShortcutAction::SplitRight, ShortcutPlatform::WindowsLinux, "ctrl-\\"),
|
shortcut(ShortcutAction::SplitRight, ShortcutPlatform::WindowsLinux, "ctrl-\\"),
|
||||||
shortcut(ShortcutAction::ToggleSidebar, ShortcutPlatform::Macos, "cmd-b"),
|
shortcut(ShortcutAction::ToggleSidebar, ShortcutPlatform::Macos, "cmd-b"),
|
||||||
@@ -245,6 +253,9 @@ impl ShortcutBinding {
|
|||||||
ShortcutAction::OpenDownloads => KeyBinding::new(self.keystroke, OpenDownloads, None),
|
ShortcutAction::OpenDownloads => KeyBinding::new(self.keystroke, OpenDownloads, None),
|
||||||
ShortcutAction::OpenHistory => KeyBinding::new(self.keystroke, OpenHistory, None),
|
ShortcutAction::OpenHistory => KeyBinding::new(self.keystroke, OpenHistory, None),
|
||||||
ShortcutAction::OpenNewTab => KeyBinding::new(self.keystroke, OpenNewTab, 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::OpenSettings => KeyBinding::new(self.keystroke, OpenSettings, None),
|
||||||
ShortcutAction::OpenTaskManager => {
|
ShortcutAction::OpenTaskManager => {
|
||||||
KeyBinding::new(self.keystroke, OpenTaskManager, None)
|
KeyBinding::new(self.keystroke, OpenTaskManager, None)
|
||||||
@@ -317,6 +328,20 @@ mod tests {
|
|||||||
assert_eq!(bindings, vec!["Cmd + ,".to_string(), "Ctrl + ,".to_string()]);
|
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]
|
#[test]
|
||||||
fn toggle_sidebar_shortcut_has_platform_bindings() {
|
fn toggle_sidebar_shortcut_has_platform_bindings() {
|
||||||
let bindings = bindings_for_action(ShortcutAction::ToggleSidebar, ShortcutPlatform::Macos)
|
let bindings = bindings_for_action(ShortcutAction::ToggleSidebar, ShortcutPlatform::Macos)
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ pub use spaces::TrashedSpace;
|
|||||||
pub struct InitialBrowserConfig {
|
pub struct InitialBrowserConfig {
|
||||||
pub space_name: String,
|
pub space_name: String,
|
||||||
pub space_icon: String,
|
pub space_icon: String,
|
||||||
|
pub space_color_hex: u32,
|
||||||
pub profile_name: String,
|
pub profile_name: String,
|
||||||
|
pub profile_color_hex: u32,
|
||||||
|
pub profile_kind: ProfileKind,
|
||||||
pub new_tab_destination: NewTabDestination,
|
pub new_tab_destination: NewTabDestination,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +55,22 @@ impl InitialBrowserConfig {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
space_name: "Work".to_string(),
|
space_name: "Work".to_string(),
|
||||||
space_icon: "W".to_string(),
|
space_icon: "W".to_string(),
|
||||||
|
space_color_hex: 0xf54e00,
|
||||||
profile_name: "Default".to_string(),
|
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(),
|
new_tab_destination: NewTabDestination::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -127,12 +145,13 @@ pub struct BrowserCore {
|
|||||||
|
|
||||||
impl BrowserCore {
|
impl BrowserCore {
|
||||||
pub fn new(config: InitialBrowserConfig) -> Result<Self, CoreError> {
|
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 active_profile_id = profile.id().clone();
|
||||||
let space = Space::new(
|
let space = Space::new(
|
||||||
config.space_name,
|
config.space_name,
|
||||||
config.space_icon,
|
config.space_icon,
|
||||||
0xf54e00,
|
config.space_color_hex,
|
||||||
active_profile_id.clone(),
|
active_profile_id.clone(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,6 +24,26 @@ fn new_private_profile_command_creates_private_profile() -> Result<(), Box<dyn E
|
|||||||
Ok(())
|
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]
|
#[test]
|
||||||
fn private_profiles_do_not_record_history() -> Result<(), Box<dyn Error>> {
|
fn private_profiles_do_not_record_history() -> Result<(), Box<dyn Error>> {
|
||||||
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
let mut core = BrowserCore::new(InitialBrowserConfig::ely_defaults()?)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user