ssh: Add tweaks to the UI (#18817)
Follow up to https://github.com/zed-industries/zed/pull/18727 --- Release Notes: - N/A
This commit is contained in:
parent
3f2de172ae
commit
af9a595770
4 changed files with 122 additions and 82 deletions
|
@ -556,23 +556,28 @@ impl DevServerProjects {
|
||||||
.w_full()
|
.w_full()
|
||||||
.border_l_1()
|
.border_l_1()
|
||||||
.border_color(cx.theme().colors().border_variant)
|
.border_color(cx.theme().colors().border_variant)
|
||||||
.my_1()
|
.mb_1()
|
||||||
.mx_1p5()
|
.mx_1p5()
|
||||||
.py_0p5()
|
.pl_2()
|
||||||
.px_3()
|
|
||||||
.child(
|
.child(
|
||||||
List::new()
|
List::new()
|
||||||
.empty_message("No projects.")
|
.empty_message("No projects.")
|
||||||
.children(ssh_connection.projects.iter().enumerate().map(|(pix, p)| {
|
.children(ssh_connection.projects.iter().enumerate().map(|(pix, p)| {
|
||||||
self.render_ssh_project(ix, &ssh_connection, pix, p, cx)
|
v_flex().gap_0p5().child(self.render_ssh_project(
|
||||||
|
ix,
|
||||||
|
&ssh_connection,
|
||||||
|
pix,
|
||||||
|
p,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
}))
|
}))
|
||||||
.child(
|
.child(
|
||||||
h_flex().child(
|
h_flex().mt_1().pl_1().child(
|
||||||
Button::new("new-remote_project", "Open Folder…")
|
Button::new("new-remote_project", "Open Folder…")
|
||||||
.icon(IconName::Plus)
|
|
||||||
.size(ButtonSize::Default)
|
.size(ButtonSize::Default)
|
||||||
.style(ButtonStyle::Filled)
|
|
||||||
.layer(ElevationIndex::ModalSurface)
|
.layer(ElevationIndex::ModalSurface)
|
||||||
|
.icon(IconName::Plus)
|
||||||
|
.icon_color(Color::Muted)
|
||||||
.icon_position(IconPosition::Start)
|
.icon_position(IconPosition::Start)
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
this.create_ssh_project(ix, ssh_connection.clone(), cx);
|
this.create_ssh_project(ix, ssh_connection.clone(), cx);
|
||||||
|
@ -593,9 +598,15 @@ impl DevServerProjects {
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let project = project.clone();
|
let project = project.clone();
|
||||||
let server = server.clone();
|
let server = server.clone();
|
||||||
|
|
||||||
ListItem::new(("remote-project", ix))
|
ListItem::new(("remote-project", ix))
|
||||||
|
.inset(true)
|
||||||
.spacing(ui::ListItemSpacing::Sparse)
|
.spacing(ui::ListItemSpacing::Sparse)
|
||||||
.start_slot(Icon::new(IconName::Folder).color(Color::Muted))
|
.start_slot(
|
||||||
|
Icon::new(IconName::Folder)
|
||||||
|
.color(Color::Muted)
|
||||||
|
.size(IconSize::Small),
|
||||||
|
)
|
||||||
.child(Label::new(project.paths.join(", ")))
|
.child(Label::new(project.paths.join(", ")))
|
||||||
.on_click(cx.listener(move |this, _, cx| {
|
.on_click(cx.listener(move |this, _, cx| {
|
||||||
let Some(app_state) = this
|
let Some(app_state) = this
|
||||||
|
@ -635,7 +646,7 @@ impl DevServerProjects {
|
||||||
.on_click(
|
.on_click(
|
||||||
cx.listener(move |this, _, cx| this.delete_ssh_project(server_ix, ix, cx)),
|
cx.listener(move |this, _, cx| this.delete_ssh_project(server_ix, ix, cx)),
|
||||||
)
|
)
|
||||||
.tooltip(|cx| Tooltip::text("Delete remote project", cx))
|
.tooltip(|cx| Tooltip::text("Delete Remote Project", cx))
|
||||||
.into_any_element(),
|
.into_any_element(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -709,6 +720,7 @@ impl DevServerProjects {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.id("create-dev-server")
|
.id("create-dev-server")
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
|
@ -763,6 +775,7 @@ impl DevServerProjects {
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.bg(theme.colors().editor_background)
|
.bg(theme.colors().editor_background)
|
||||||
|
.rounded_b_md()
|
||||||
.w_full()
|
.w_full()
|
||||||
.map(|this| {
|
.map(|this| {
|
||||||
if let Some(ssh_prompt) = ssh_prompt {
|
if let Some(ssh_prompt) = ssh_prompt {
|
||||||
|
@ -773,9 +786,8 @@ impl DevServerProjects {
|
||||||
h_flex()
|
h_flex()
|
||||||
.p_2()
|
.p_2()
|
||||||
.w_full()
|
.w_full()
|
||||||
.content_center()
|
.justify_center()
|
||||||
.gap_2()
|
.gap_1p5()
|
||||||
.child(h_flex().w_full())
|
|
||||||
.child(
|
.child(
|
||||||
div().p_1().rounded_lg().bg(color).with_animation(
|
div().p_1().rounded_lg().bg(color).with_animation(
|
||||||
"pulse-ssh-waiting-for-connection",
|
"pulse-ssh-waiting-for-connection",
|
||||||
|
@ -788,8 +800,7 @@ impl DevServerProjects {
|
||||||
.child(
|
.child(
|
||||||
Label::new("Waiting for connection…")
|
Label::new("Waiting for connection…")
|
||||||
.size(LabelSize::Small),
|
.size(LabelSize::Small),
|
||||||
)
|
),
|
||||||
.child(h_flex().w_full()),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -566,7 +566,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
.py_2()
|
.py_2()
|
||||||
.pr_2()
|
.pr_2()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border_variant)
|
||||||
.justify_end()
|
.justify_end()
|
||||||
.gap_4()
|
.gap_4()
|
||||||
.child(
|
.child(
|
||||||
|
@ -574,7 +574,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||||
.when_some(KeyBinding::for_action(&OpenRemote, cx), |button, key| {
|
.when_some(KeyBinding::for_action(&OpenRemote, cx), |button, key| {
|
||||||
button.child(key)
|
button.child(key)
|
||||||
})
|
})
|
||||||
.child(Label::new("Open remote folder…").color(Color::Muted))
|
.child(Label::new("Open Remote Folder…").color(Color::Muted))
|
||||||
.on_click(|_, cx| cx.dispatch_action(OpenRemote.boxed_clone())),
|
.on_click(|_, cx| cx.dispatch_action(OpenRemote.boxed_clone())),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
@ -583,7 +583,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||||
KeyBinding::for_action(&workspace::Open, cx),
|
KeyBinding::for_action(&workspace::Open, cx),
|
||||||
|button, key| button.child(key),
|
|button, key| button.child(key),
|
||||||
)
|
)
|
||||||
.child(Label::new("Open local folder…").color(Color::Muted))
|
.child(Label::new("Open Local Folder…").color(Color::Muted))
|
||||||
.on_click(|_, cx| cx.dispatch_action(workspace::Open.boxed_clone())),
|
.on_click(|_, cx| cx.dispatch_action(workspace::Open.boxed_clone())),
|
||||||
)
|
)
|
||||||
.into_any(),
|
.into_any(),
|
||||||
|
|
|
@ -16,9 +16,9 @@ use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{Settings, SettingsSources};
|
use settings::{Settings, SettingsSources};
|
||||||
use ui::{
|
use ui::{
|
||||||
div, h_flex, v_flex, ActiveTheme, ButtonCommon, Clickable, Color, FluentBuilder as _, Icon,
|
div, h_flex, prelude::*, v_flex, ActiveTheme, ButtonCommon, Clickable, Color, Icon, IconButton,
|
||||||
IconButton, IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon, Styled,
|
IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon, Styled, Tooltip,
|
||||||
StyledExt as _, Tooltip, ViewContext, VisualContext, WindowContext,
|
ViewContext, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
use workspace::{AppState, ModalView, Workspace};
|
use workspace::{AppState, ModalView, Workspace};
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ pub struct SshPrompt {
|
||||||
pub struct SshConnectionModal {
|
pub struct SshConnectionModal {
|
||||||
pub(crate) prompt: View<SshPrompt>,
|
pub(crate) prompt: View<SshPrompt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SshPrompt {
|
impl SshPrompt {
|
||||||
pub fn new(connection_options: &SshConnectionOptions, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(connection_options: &SshConnectionOptions, cx: &mut ViewContext<Self>) -> Self {
|
||||||
let connection_string = connection_options.connection_string().into();
|
let connection_string = connection_options.connection_string().into();
|
||||||
|
@ -136,57 +137,70 @@ impl SshPrompt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for SshPrompt {
|
impl Render for SshPrompt {
|
||||||
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let cx = cx.window_context();
|
||||||
|
let theme = cx.theme();
|
||||||
v_flex()
|
v_flex()
|
||||||
.w_full()
|
|
||||||
.key_context("PasswordPrompt")
|
.key_context("PasswordPrompt")
|
||||||
.justify_start()
|
.size_full()
|
||||||
|
.justify_center()
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
h_flex()
|
||||||
.p_4()
|
.py_2()
|
||||||
.size_full()
|
.px_4()
|
||||||
.child(
|
.justify_center()
|
||||||
h_flex()
|
.child(if self.error_message.is_some() {
|
||||||
.gap_2()
|
Icon::new(IconName::XCircle)
|
||||||
.justify_between()
|
.size(IconSize::Medium)
|
||||||
.child(h_flex().w_full())
|
.color(Color::Error)
|
||||||
.child(if self.error_message.is_some() {
|
.into_any_element()
|
||||||
Icon::new(IconName::XCircle)
|
} else {
|
||||||
.size(IconSize::Medium)
|
Icon::new(IconName::ArrowCircle)
|
||||||
.color(Color::Error)
|
.size(IconSize::Medium)
|
||||||
.into_any_element()
|
.with_animation(
|
||||||
} else {
|
"arrow-circle",
|
||||||
Icon::new(IconName::ArrowCircle)
|
Animation::new(Duration::from_secs(2)).repeat(),
|
||||||
.size(IconSize::Medium)
|
|icon, delta| {
|
||||||
.with_animation(
|
icon.transform(Transformation::rotate(percentage(delta)))
|
||||||
"arrow-circle",
|
},
|
||||||
Animation::new(Duration::from_secs(2)).repeat(),
|
)
|
||||||
|icon, delta| {
|
.into_any_element()
|
||||||
icon.transform(Transformation::rotate(percentage(
|
|
||||||
delta,
|
|
||||||
)))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
})
|
|
||||||
.child(Label::new(format!(
|
|
||||||
"Connecting to {}…",
|
|
||||||
self.connection_string
|
|
||||||
)))
|
|
||||||
.child(h_flex().w_full()),
|
|
||||||
)
|
|
||||||
.when_some(self.error_message.as_ref(), |el, error| {
|
|
||||||
el.child(Label::new(error.clone()))
|
|
||||||
})
|
})
|
||||||
.when(
|
.child(
|
||||||
self.error_message.is_none() && self.status_message.is_some(),
|
div()
|
||||||
|el| el.child(Label::new(self.status_message.clone().unwrap())),
|
.ml_1()
|
||||||
|
.child(Label::new("SSH Connection").size(LabelSize::Small)),
|
||||||
)
|
)
|
||||||
.when_some(self.prompt.as_ref(), |el, prompt| {
|
.child(
|
||||||
el.child(Label::new(prompt.0.clone()))
|
div()
|
||||||
.child(self.editor.clone())
|
.when_some(self.error_message.as_ref(), |el, error| {
|
||||||
}),
|
el.child(Label::new(format!("-{}", error)).size(LabelSize::Small))
|
||||||
|
})
|
||||||
|
.when(
|
||||||
|
self.error_message.is_none() && self.status_message.is_some(),
|
||||||
|
|el| {
|
||||||
|
el.child(
|
||||||
|
Label::new(format!(
|
||||||
|
"-{}",
|
||||||
|
self.status_message.clone().unwrap()
|
||||||
|
))
|
||||||
|
.size(LabelSize::Small),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
.child(div().when_some(self.prompt.as_ref(), |el, prompt| {
|
||||||
|
el.child(
|
||||||
|
h_flex()
|
||||||
|
.p_4()
|
||||||
|
.border_t_1()
|
||||||
|
.border_color(theme.colors().border_variant)
|
||||||
|
.font_buffer(cx)
|
||||||
|
.child(Label::new(prompt.0.clone()))
|
||||||
|
.child(self.editor.clone()),
|
||||||
|
)
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,39 +224,54 @@ impl Render for SshConnectionModal {
|
||||||
fn render(&mut self, cx: &mut ui::ViewContext<Self>) -> impl ui::IntoElement {
|
fn render(&mut self, cx: &mut ui::ViewContext<Self>) -> impl ui::IntoElement {
|
||||||
let connection_string = self.prompt.read(cx).connection_string.clone();
|
let connection_string = self.prompt.read(cx).connection_string.clone();
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
let header_color = theme.colors().element_background;
|
let mut header_color = cx.theme().colors().text;
|
||||||
let body_color = theme.colors().background;
|
header_color.fade_out(0.96);
|
||||||
|
let body_color = theme.colors().editor_background;
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.elevation_3(cx)
|
.elevation_3(cx)
|
||||||
.on_action(cx.listener(Self::dismiss))
|
.on_action(cx.listener(Self::dismiss))
|
||||||
.on_action(cx.listener(Self::confirm))
|
.on_action(cx.listener(Self::confirm))
|
||||||
.w(px(400.))
|
.w(px(500.))
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.colors().border)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
.relative()
|
||||||
.p_1()
|
.p_1()
|
||||||
|
.rounded_t_md()
|
||||||
.border_b_1()
|
.border_b_1()
|
||||||
.border_color(theme.colors().border)
|
.border_color(theme.colors().border)
|
||||||
.bg(header_color)
|
.bg(header_color)
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.child(
|
.child(
|
||||||
IconButton::new("ssh-connection-cancel", IconName::ArrowLeft)
|
div().absolute().left_0p5().top_0p5().child(
|
||||||
.icon_size(IconSize::XSmall)
|
IconButton::new("ssh-connection-cancel", IconName::ArrowLeft)
|
||||||
.on_click(|_, cx| cx.dispatch_action(menu::Cancel.boxed_clone()))
|
.icon_size(IconSize::XSmall)
|
||||||
.tooltip(|cx| Tooltip::for_action("Back", &menu::Cancel, cx)),
|
.on_click(|_, cx| cx.dispatch_action(menu::Cancel.boxed_clone()))
|
||||||
|
.tooltip(|cx| Tooltip::for_action("Back", &menu::Cancel, cx)),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
|
.w_full()
|
||||||
.gap_2()
|
.gap_2()
|
||||||
|
.justify_center()
|
||||||
.child(Icon::new(IconName::Server).size(IconSize::XSmall))
|
.child(Icon::new(IconName::Server).size(IconSize::XSmall))
|
||||||
.child(
|
.child(
|
||||||
Label::new(connection_string)
|
Label::new(connection_string)
|
||||||
.size(ui::LabelSize::Small)
|
.size(ui::LabelSize::Small)
|
||||||
.single_line(),
|
.single_line(),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
.child(div()),
|
)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.rounded_b_md()
|
||||||
|
.bg(body_color)
|
||||||
|
.w_full()
|
||||||
|
.child(self.prompt.clone()),
|
||||||
)
|
)
|
||||||
.child(h_flex().bg(body_color).w_full().child(self.prompt.clone()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ use smallvec::SmallVec;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::{
|
use ui::{
|
||||||
h_flex, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon, IconName,
|
h_flex, prelude::*, Avatar, Button, ButtonLike, ButtonStyle, ContextMenu, Icon,
|
||||||
Indicator, PopoverMenu, Tooltip,
|
IconButtonShape, IconName, IconSize, Indicator, PopoverMenu, Tooltip,
|
||||||
};
|
};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use vcs_menu::{BranchList, OpenRecent as ToggleVcsMenu};
|
use vcs_menu::{BranchList, OpenRecent as ToggleVcsMenu};
|
||||||
|
@ -274,18 +274,19 @@ impl TitleBar {
|
||||||
};
|
};
|
||||||
let indicator = div()
|
let indicator = div()
|
||||||
.absolute()
|
.absolute()
|
||||||
.w_1_4()
|
.size_1p5()
|
||||||
.h_1_4()
|
|
||||||
.right_0p5()
|
.right_0p5()
|
||||||
.bottom_0p5()
|
.bottom_0p5()
|
||||||
.p_1()
|
.rounded_full()
|
||||||
.rounded_2xl()
|
|
||||||
.bg(indicator_color.color(cx));
|
.bg(indicator_color.color(cx));
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
div()
|
div()
|
||||||
|
.relative()
|
||||||
.child(
|
.child(
|
||||||
IconButton::new("ssh-server-icon", IconName::Server)
|
IconButton::new("ssh-server-icon", IconName::Server)
|
||||||
|
.icon_size(IconSize::Small)
|
||||||
|
.shape(IconButtonShape::Square)
|
||||||
.tooltip(move |cx| {
|
.tooltip(move |cx| {
|
||||||
Tooltip::with_meta(
|
Tooltip::with_meta(
|
||||||
"Remote Project",
|
"Remote Project",
|
||||||
|
@ -294,7 +295,6 @@ impl TitleBar {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.shape(ui::IconButtonShape::Square)
|
|
||||||
.on_click(|_, cx| {
|
.on_click(|_, cx| {
|
||||||
cx.dispatch_action(OpenRemote.boxed_clone());
|
cx.dispatch_action(OpenRemote.boxed_clone());
|
||||||
}),
|
}),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue