Merge branch 'main' into add-collab-tests

This commit is contained in:
Mikayla 2023-11-08 09:57:08 -08:00
commit 9b30f490c7
No known key found for this signature in database
171 changed files with 50671 additions and 7748 deletions

View file

@ -15,8 +15,8 @@ use futures::{
TryStreamExt,
};
use gpui::{
serde_json, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model, SemanticVersion, Task,
WeakModel,
actions, serde_json, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model,
SemanticVersion, Task, WeakModel,
};
use lazy_static::lazy_static;
use parking_lot::RwLock;
@ -70,14 +70,7 @@ pub const ZED_SECRET_CLIENT_TOKEN: &str = "618033988749894";
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100);
pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);
#[derive(Clone, Default, PartialEq, Deserialize)]
pub struct SignIn;
#[derive(Clone, Default, PartialEq, Deserialize)]
pub struct SignOut;
#[derive(Clone, Default, PartialEq, Deserialize)]
pub struct Reconnect;
actions!(SignIn, SignOut, Reconnect);
pub fn init_settings(cx: &mut AppContext) {
TelemetrySettings::register(cx);
@ -87,7 +80,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
init_settings(cx);
let client = Arc::downgrade(client);
cx.register_action_type::<SignIn>();
cx.on_action({
let client = client.clone();
move |_: &SignIn, cx| {
@ -100,7 +92,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
}
});
cx.register_action_type::<SignOut>();
cx.on_action({
let client = client.clone();
move |_: &SignOut, cx| {
@ -113,7 +104,6 @@ pub fn init(client: &Arc<Client>, cx: &mut AppContext) {
}
});
cx.register_action_type::<Reconnect>();
cx.on_action({
let client = client.clone();
move |_: &Reconnect, cx| {

View file

@ -7,8 +7,8 @@ use async_tar::Archive;
use collections::{HashMap, HashSet};
use futures::{channel::oneshot, future::Shared, Future, FutureExt, TryFutureExt};
use gpui::{
AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model, ModelContext,
Task, WeakModel,
actions, AppContext, AsyncAppContext, Context, Entity, EntityId, EventEmitter, Model,
ModelContext, Task, WeakModel,
};
use language::{
language_settings::{all_language_settings, language_settings},
@ -34,19 +34,11 @@ use util::{
// todo!()
// const COPILOT_AUTH_NAMESPACE: &'static str = "copilot_auth";
// actions!(copilot_auth, [SignIn, SignOut]);
actions!(SignIn, SignOut);
// todo!()
// const COPILOT_NAMESPACE: &'static str = "copilot";
// actions!(
// copilot,
// [Suggest, NextSuggestion, PreviousSuggestion, Reinstall]
// );
//
pub struct Suggest;
pub struct NextSuggestion;
pub struct PreviousSuggestion;
pub struct Reinstall;
actions!(Suggest, NextSuggestion, PreviousSuggestion, Reinstall);
pub fn init(
new_server_id: LanguageServerId,

View file

@ -31,7 +31,7 @@ drag_and_drop = { path = "../drag_and_drop" }
collections = { path = "../collections" }
# context_menu = { path = "../context_menu" }
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
git = { path = "../git" }
git = { package = "git3", path = "../git3" }
gpui = { package = "gpui2", path = "../gpui2" }
language = { package = "language2", path = "../language2" }
lsp = { package = "lsp2", path = "../lsp2" }

View file

@ -4,6 +4,7 @@ mod inlay_map;
mod tab_map;
mod wrap_map;
use crate::EditorStyle;
use crate::{
link_go_to_definition::InlayHighlight, movement::TextLayoutDetails, Anchor, AnchorRangeExt,
InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
@ -11,14 +12,18 @@ use crate::{
pub use block_map::{BlockMap, BlockPoint};
use collections::{BTreeMap, HashMap, HashSet};
use fold_map::FoldMap;
use gpui::{Font, FontId, HighlightStyle, Hsla, Line, Model, ModelContext, Pixels};
use gpui::{
Font, FontId, HighlightStyle, Hsla, Line, Model, ModelContext, Pixels, TextRun, UnderlineStyle,
};
use inlay_map::InlayMap;
use language::{
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
};
use lsp::DiagnosticSeverity;
use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
use sum_tree::{Bias, TreeMap};
use tab_map::TabMap;
use theme::{SyntaxTheme, Theme};
use wrap_map::WrapMap;
pub use block_map::{
@ -35,6 +40,8 @@ pub enum FoldStatus {
Foldable,
}
const UNNECESSARY_CODE_FADE: f32 = 0.3;
pub trait ToDisplayPoint {
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
}
@ -496,63 +503,63 @@ impl DisplaySnapshot {
)
}
// pub fn highlighted_chunks<'a>(
// &'a self,
// display_rows: Range<u32>,
// language_aware: bool,
// style: &'a EditorStyle,
// ) -> impl Iterator<Item = HighlightedChunk<'a>> {
// self.chunks(
// display_rows,
// language_aware,
// Some(style.theme.hint),
// Some(style.theme.suggestion),
// )
// .map(|chunk| {
// let mut highlight_style = chunk
// .syntax_highlight_id
// .and_then(|id| id.style(&style.syntax));
pub fn highlighted_chunks<'a>(
&'a self,
display_rows: Range<u32>,
language_aware: bool,
editor_style: &'a EditorStyle,
) -> impl Iterator<Item = HighlightedChunk<'a>> {
self.chunks(
display_rows,
language_aware,
Some(editor_style.syntax.inlay_style),
Some(editor_style.syntax.suggestion_style),
)
.map(|chunk| {
let mut highlight_style = chunk
.syntax_highlight_id
.and_then(|id| id.style(&editor_style.syntax));
// if let Some(chunk_highlight) = chunk.highlight_style {
// if let Some(highlight_style) = highlight_style.as_mut() {
// highlight_style.highlight(chunk_highlight);
// } else {
// highlight_style = Some(chunk_highlight);
// }
// }
if let Some(chunk_highlight) = chunk.highlight_style {
if let Some(highlight_style) = highlight_style.as_mut() {
highlight_style.highlight(chunk_highlight);
} else {
highlight_style = Some(chunk_highlight);
}
}
// let mut diagnostic_highlight = HighlightStyle::default();
let mut diagnostic_highlight = HighlightStyle::default();
// if chunk.is_unnecessary {
// diagnostic_highlight.fade_out = Some(style.unnecessary_code_fade);
// }
if chunk.is_unnecessary {
diagnostic_highlight.fade_out = Some(UNNECESSARY_CODE_FADE);
}
// if let Some(severity) = chunk.diagnostic_severity {
// // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
// if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
// todo!()
// // let diagnostic_style = super::diagnostic_style(severity, true, style);
// // diagnostic_highlight.underline = Some(UnderlineStyle {
// // color: Some(diagnostic_style.message.text.color),
// // thickness: 1.0.into(),
// // wavy: true,
// // });
// }
// }
if let Some(severity) = chunk.diagnostic_severity {
// Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
let diagnostic_color =
super::diagnostic_style(severity, true, &editor_style.diagnostic_style);
diagnostic_highlight.underline = Some(UnderlineStyle {
color: Some(diagnostic_color),
thickness: 1.0.into(),
wavy: true,
});
}
}
// if let Some(highlight_style) = highlight_style.as_mut() {
// highlight_style.highlight(diagnostic_highlight);
// } else {
// highlight_style = Some(diagnostic_highlight);
// }
if let Some(highlight_style) = highlight_style.as_mut() {
highlight_style.highlight(diagnostic_highlight);
} else {
highlight_style = Some(diagnostic_highlight);
}
// HighlightedChunk {
// chunk: chunk.text,
// style: highlight_style,
// is_tab: chunk.is_tab,
// }
// })
// }
HighlightedChunk {
chunk: chunk.text,
style: highlight_style,
is_tab: chunk.is_tab,
}
})
}
pub fn lay_out_line_for_row(
&self,
@ -560,53 +567,46 @@ impl DisplaySnapshot {
TextLayoutDetails {
text_system,
editor_style,
rem_size,
}: &TextLayoutDetails,
) -> Line {
todo!()
// let mut styles = Vec::new();
// let mut line = String::new();
// let mut ended_in_newline = false;
let mut runs = Vec::new();
let mut line = String::new();
let mut ended_in_newline = false;
// let range = display_row..display_row + 1;
// for chunk in self.highlighted_chunks(range, false, editor_style) {
// line.push_str(chunk.chunk);
let range = display_row..display_row + 1;
for chunk in self.highlighted_chunks(range, false, &editor_style) {
line.push_str(chunk.chunk);
// let text_style = if let Some(style) = chunk.style {
// editor_style
// .text
// .clone()
// .highlight(style, text_system)
// .map(Cow::Owned)
// .unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
// } else {
// Cow::Borrowed(&editor_style.text)
// };
// ended_in_newline = chunk.chunk.ends_with("\n");
let text_style = if let Some(style) = chunk.style {
editor_style
.text
.clone()
.highlight(style)
.map(Cow::Owned)
.unwrap_or_else(|_| Cow::Borrowed(&editor_style.text))
} else {
Cow::Borrowed(&editor_style.text)
};
ended_in_newline = chunk.chunk.ends_with("\n");
// styles.push(
// todo!(), // len: chunk.chunk.len(),
// // font_id: text_style.font_id,
// // color: text_style.color,
// // underline: text_style.underline,
// );
// }
runs.push(text_style.to_run(chunk.chunk.len()))
}
// // our pixel positioning logic assumes each line ends in \n,
// // this is almost always true except for the last line which
// // may have no trailing newline.
// if !ended_in_newline && display_row == self.max_point().row() {
// line.push_str("\n");
// our pixel positioning logic assumes each line ends in \n,
// this is almost always true except for the last line which
// may have no trailing newline.
if !ended_in_newline && display_row == self.max_point().row() {
line.push_str("\n");
runs.push(editor_style.text.to_run("\n".len()));
}
// todo!();
// // styles.push(RunStyle {
// // len: "\n".len(),
// // font_id: editor_style.text.font_id,
// // color: editor_style.text_color,
// // underline: editor_style.text.underline,
// // });
// }
// text_system.layout_text(&line, editor_style.text.font_size, &styles, None)
let font_size = editor_style.text.font_size.to_pixels(*rem_size);
text_system
.layout_text(&line, font_size, &runs, None)
.unwrap()
.pop()
.unwrap()
}
pub fn x_for_point(

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,7 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
opening_range.to_anchors(&snapshot.buffer_snapshot),
closing_range.to_anchors(&snapshot.buffer_snapshot),
],
|theme| todo!("theme.editor.document_highlight_read_background"),
|theme| theme.editor_document_highlight_read_background,
cx,
)
}

View file

@ -144,8 +144,7 @@ pub fn hide_hover(editor: &mut Editor, cx: &mut ViewContext<Editor>) -> bool {
editor.hover_state.info_task = None;
editor.hover_state.triggered_from = None;
// todo!()
// editor.clear_background_highlights::<HoverState>(cx);
editor.clear_background_highlights::<HoverState>(cx);
if did_hide {
cx.notify();
@ -325,23 +324,22 @@ fn show_hover(
};
this.update(&mut cx, |this, cx| {
todo!();
// if let Some(symbol_range) = hover_popover
// .as_ref()
// .and_then(|hover_popover| hover_popover.symbol_range.as_text_range())
// {
// // Highlight the selected symbol using a background highlight
// this.highlight_background::<HoverState>(
// vec![symbol_range],
// |theme| theme.editor.hover_popover.highlight,
// cx,
// );
// } else {
// this.clear_background_highlights::<HoverState>(cx);
// }
//
// this.hover_state.info_popover = hover_popover;
// cx.notify();
if let Some(symbol_range) = hover_popover
.as_ref()
.and_then(|hover_popover| hover_popover.symbol_range.as_text_range())
{
// Highlight the selected symbol using a background highlight
this.highlight_background::<HoverState>(
vec![symbol_range],
|theme| theme.element_hover, // todo! update theme
cx,
);
} else {
this.clear_background_highlights::<HoverState>(cx);
}
this.hover_state.info_popover = hover_popover;
cx.notify();
})?;
Ok::<_, anyhow::Error>(())

View file

@ -27,7 +27,7 @@ use std::{
sync::Arc,
};
use text::Selection;
use theme::{ActiveTheme, ThemeVariant};
use theme::{ActiveTheme, Theme};
use util::{paths::PathExt, ResultExt, TryFutureExt};
use workspace::item::{BreadcrumbText, FollowableItemHandle};
use workspace::{
@ -159,16 +159,14 @@ impl FollowableItem for Editor {
self.buffer.update(cx, |buffer, cx| {
buffer.remove_active_selections(cx);
});
} else {
} else if self.focus_handle.is_focused(cx) {
self.buffer.update(cx, |buffer, cx| {
if self.focused {
buffer.set_active_selections(
&self.selections.disjoint_anchors(),
self.selections.line_mode,
self.cursor_shape,
cx,
);
}
buffer.set_active_selections(
&self.selections.disjoint_anchors(),
self.selections.line_mode,
self.cursor_shape,
cx,
);
});
}
cx.notify();
@ -779,7 +777,7 @@ impl Item for Editor {
ToolbarItemLocation::PrimaryLeft { flex: None }
}
fn breadcrumbs(&self, variant: &ThemeVariant, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
fn breadcrumbs(&self, variant: &Theme, cx: &AppContext) -> Option<Vec<BreadcrumbText>> {
todo!();
// let cursor = self.selections.newest_anchor().head();
// let multibuffer = &self.buffer().read(cx);

View file

@ -171,173 +171,170 @@ pub fn update_inlay_link_and_hover_points(
shift_held: bool,
cx: &mut ViewContext<'_, Editor>,
) {
todo!("old implementation below")
}
// ) {
// let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
// Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))
// } else {
// None
// };
// let mut go_to_definition_updated = false;
// let mut hover_updated = false;
// if let Some(hovered_offset) = hovered_offset {
// let buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
// let previous_valid_anchor = buffer_snapshot.anchor_at(
// point_for_position.previous_valid.to_point(snapshot),
// Bias::Left,
// );
// let next_valid_anchor = buffer_snapshot.anchor_at(
// point_for_position.next_valid.to_point(snapshot),
// Bias::Right,
// );
// if let Some(hovered_hint) = editor
// .visible_inlay_hints(cx)
// .into_iter()
// .skip_while(|hint| {
// hint.position
// .cmp(&previous_valid_anchor, &buffer_snapshot)
// .is_lt()
// })
// .take_while(|hint| {
// hint.position
// .cmp(&next_valid_anchor, &buffer_snapshot)
// .is_le()
// })
// .max_by_key(|hint| hint.id)
// {
// let inlay_hint_cache = editor.inlay_hint_cache();
// let excerpt_id = previous_valid_anchor.excerpt_id;
// if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) {
// match cached_hint.resolve_state {
// ResolveState::CanResolve(_, _) => {
// if let Some(buffer_id) = previous_valid_anchor.buffer_id {
// inlay_hint_cache.spawn_hint_resolve(
// buffer_id,
// excerpt_id,
// hovered_hint.id,
// cx,
// );
// }
// }
// ResolveState::Resolved => {
// let mut extra_shift_left = 0;
// let mut extra_shift_right = 0;
// if cached_hint.padding_left {
// extra_shift_left += 1;
// extra_shift_right += 1;
// }
// if cached_hint.padding_right {
// extra_shift_right += 1;
// }
// match cached_hint.label {
// project::InlayHintLabel::String(_) => {
// if let Some(tooltip) = cached_hint.tooltip {
// hover_popover::hover_at_inlay(
// editor,
// InlayHover {
// excerpt: excerpt_id,
// tooltip: match tooltip {
// InlayHintTooltip::String(text) => HoverBlock {
// text,
// kind: HoverBlockKind::PlainText,
// },
// InlayHintTooltip::MarkupContent(content) => {
// HoverBlock {
// text: content.value,
// kind: content.kind,
// }
// }
// },
// range: InlayHighlight {
// inlay: hovered_hint.id,
// inlay_position: hovered_hint.position,
// range: extra_shift_left
// ..hovered_hint.text.len() + extra_shift_right,
// },
// },
// cx,
// );
// hover_updated = true;
// }
// }
// project::InlayHintLabel::LabelParts(label_parts) => {
// let hint_start =
// snapshot.anchor_to_inlay_offset(hovered_hint.position);
// if let Some((hovered_hint_part, part_range)) =
// hover_popover::find_hovered_hint_part(
// label_parts,
// hint_start,
// hovered_offset,
// )
// {
// let highlight_start =
// (part_range.start - hint_start).0 + extra_shift_left;
// let highlight_end =
// (part_range.end - hint_start).0 + extra_shift_right;
// let highlight = InlayHighlight {
// inlay: hovered_hint.id,
// inlay_position: hovered_hint.position,
// range: highlight_start..highlight_end,
// };
// if let Some(tooltip) = hovered_hint_part.tooltip {
// hover_popover::hover_at_inlay(
// editor,
// InlayHover {
// excerpt: excerpt_id,
// tooltip: match tooltip {
// InlayHintLabelPartTooltip::String(text) => {
// HoverBlock {
// text,
// kind: HoverBlockKind::PlainText,
// }
// }
// InlayHintLabelPartTooltip::MarkupContent(
// content,
// ) => HoverBlock {
// text: content.value,
// kind: content.kind,
// },
// },
// range: highlight.clone(),
// },
// cx,
// );
// hover_updated = true;
// }
// if let Some((language_server_id, location)) =
// hovered_hint_part.location
// {
// go_to_definition_updated = true;
// update_go_to_definition_link(
// editor,
// Some(GoToDefinitionTrigger::InlayHint(
// highlight,
// location,
// language_server_id,
// )),
// cmd_held,
// shift_held,
// cx,
// );
// }
// }
// }
// };
// }
// ResolveState::Resolving => {}
// }
// }
// }
// }
let hovered_offset = if point_for_position.column_overshoot_after_line_end == 0 {
Some(snapshot.display_point_to_inlay_offset(point_for_position.exact_unclipped, Bias::Left))
} else {
None
};
let mut go_to_definition_updated = false;
let mut hover_updated = false;
if let Some(hovered_offset) = hovered_offset {
let buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
let previous_valid_anchor = buffer_snapshot.anchor_at(
point_for_position.previous_valid.to_point(snapshot),
Bias::Left,
);
let next_valid_anchor = buffer_snapshot.anchor_at(
point_for_position.next_valid.to_point(snapshot),
Bias::Right,
);
if let Some(hovered_hint) = editor
.visible_inlay_hints(cx)
.into_iter()
.skip_while(|hint| {
hint.position
.cmp(&previous_valid_anchor, &buffer_snapshot)
.is_lt()
})
.take_while(|hint| {
hint.position
.cmp(&next_valid_anchor, &buffer_snapshot)
.is_le()
})
.max_by_key(|hint| hint.id)
{
let inlay_hint_cache = editor.inlay_hint_cache();
let excerpt_id = previous_valid_anchor.excerpt_id;
if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) {
match cached_hint.resolve_state {
ResolveState::CanResolve(_, _) => {
if let Some(buffer_id) = previous_valid_anchor.buffer_id {
inlay_hint_cache.spawn_hint_resolve(
buffer_id,
excerpt_id,
hovered_hint.id,
cx,
);
}
}
ResolveState::Resolved => {
let mut extra_shift_left = 0;
let mut extra_shift_right = 0;
if cached_hint.padding_left {
extra_shift_left += 1;
extra_shift_right += 1;
}
if cached_hint.padding_right {
extra_shift_right += 1;
}
match cached_hint.label {
project::InlayHintLabel::String(_) => {
if let Some(tooltip) = cached_hint.tooltip {
hover_popover::hover_at_inlay(
editor,
InlayHover {
excerpt: excerpt_id,
tooltip: match tooltip {
InlayHintTooltip::String(text) => HoverBlock {
text,
kind: HoverBlockKind::PlainText,
},
InlayHintTooltip::MarkupContent(content) => {
HoverBlock {
text: content.value,
kind: content.kind,
}
}
},
range: InlayHighlight {
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: extra_shift_left
..hovered_hint.text.len() + extra_shift_right,
},
},
cx,
);
hover_updated = true;
}
}
project::InlayHintLabel::LabelParts(label_parts) => {
let hint_start =
snapshot.anchor_to_inlay_offset(hovered_hint.position);
if let Some((hovered_hint_part, part_range)) =
hover_popover::find_hovered_hint_part(
label_parts,
hint_start,
hovered_offset,
)
{
let highlight_start =
(part_range.start - hint_start).0 + extra_shift_left;
let highlight_end =
(part_range.end - hint_start).0 + extra_shift_right;
let highlight = InlayHighlight {
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: highlight_start..highlight_end,
};
if let Some(tooltip) = hovered_hint_part.tooltip {
hover_popover::hover_at_inlay(
editor,
InlayHover {
excerpt: excerpt_id,
tooltip: match tooltip {
InlayHintLabelPartTooltip::String(text) => {
HoverBlock {
text,
kind: HoverBlockKind::PlainText,
}
}
InlayHintLabelPartTooltip::MarkupContent(
content,
) => HoverBlock {
text: content.value,
kind: content.kind,
},
},
range: highlight.clone(),
},
cx,
);
hover_updated = true;
}
if let Some((language_server_id, location)) =
hovered_hint_part.location
{
go_to_definition_updated = true;
update_go_to_definition_link(
editor,
Some(GoToDefinitionTrigger::InlayHint(
highlight,
location,
language_server_id,
)),
cmd_held,
shift_held,
cx,
);
}
}
}
};
}
ResolveState::Resolving => {}
}
}
}
}
// if !go_to_definition_updated {
// update_go_to_definition_link(editor, None, cmd_held, shift_held, cx);
// }
// if !hover_updated {
// hover_popover::hover_at(editor, None, cx);
// }
// }
if !go_to_definition_updated {
update_go_to_definition_link(editor, None, cmd_held, shift_held, cx);
}
if !hover_updated {
hover_popover::hover_at(editor, None, cx);
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LinkDefinitionKind {
@ -581,7 +578,7 @@ fn go_to_fetched_definition_of_kind(
let is_correct_kind = cached_definitions_kind == Some(kind);
if !cached_definitions.is_empty() && is_correct_kind {
if !editor.focused {
if !editor.focus_handle.is_focused(cx) {
cx.focus(&editor.focus_handle);
}

View file

@ -1,9 +1,9 @@
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{char_kind, CharKind, EditorStyle, ToOffset, ToPoint};
use gpui::{px, TextSystem};
use gpui::{px, Pixels, TextSystem};
use language::Point;
use serde::de::IntoDeserializer;
use std::ops::Range;
use std::{ops::Range, sync::Arc};
#[derive(Debug, PartialEq)]
pub enum FindRange {
@ -14,8 +14,9 @@ pub enum FindRange {
/// TextLayoutDetails encompasses everything we need to move vertically
/// taking into account variable width characters.
pub struct TextLayoutDetails {
pub text_system: TextSystem,
pub text_system: Arc<TextSystem>,
pub editor_style: EditorStyle,
pub rem_size: Pixels,
}
pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> DisplayPoint {

View file

@ -288,16 +288,15 @@ impl ScrollManager {
}
}
// todo!()
impl Editor {
// pub fn vertical_scroll_margin(&mut self) -> usize {
// self.scroll_manager.vertical_scroll_margin as usize
// }
pub fn vertical_scroll_margin(&mut self) -> usize {
self.scroll_manager.vertical_scroll_margin as usize
}
// pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
// self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
// cx.notify();
// }
pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
self.scroll_manager.vertical_scroll_margin = margin_rows as f32;
cx.notify();
}
pub fn visible_line_count(&self) -> Option<f32> {
self.scroll_manager.visible_line_count
@ -349,10 +348,10 @@ impl Editor {
self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx);
}
// pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<Pixels> {
// let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
// self.scroll_manager.anchor.scroll_position(&display_map)
// }
pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> gpui::Point<f32> {
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
self.scroll_manager.anchor.scroll_position(&display_map)
}
pub fn set_scroll_anchor(&mut self, scroll_anchor: ScrollAnchor, cx: &mut ViewContext<Self>) {
hide_hover(self, cx);
@ -380,50 +379,50 @@ impl Editor {
.set_anchor(scroll_anchor, top_row, false, false, workspace_id, cx);
}
// pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
// if matches!(self.mode, EditorMode::SingleLine) {
// cx.propagate_action();
// return;
// }
pub fn scroll_screen(&mut self, amount: &ScrollAmount, cx: &mut ViewContext<Self>) {
if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate();
return;
}
// if self.take_rename(true, cx).is_some() {
// return;
// }
if self.take_rename(true, cx).is_some() {
return;
}
// let cur_position = self.scroll_position(cx);
// let new_pos = cur_position + point(0., amount.lines(self));
// self.set_scroll_position(new_pos, cx);
// }
let cur_position = self.scroll_position(cx);
let new_pos = cur_position + point(0., amount.lines(self));
self.set_scroll_position(new_pos, cx);
}
// /// Returns an ordering. The newest selection is:
// /// Ordering::Equal => on screen
// /// Ordering::Less => above the screen
// /// Ordering::Greater => below the screen
// pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
// let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
// let newest_head = self
// .selections
// .newest_anchor()
// .head()
// .to_display_point(&snapshot);
// let screen_top = self
// .scroll_manager
// .anchor
// .anchor
// .to_display_point(&snapshot);
/// Returns an ordering. The newest selection is:
/// Ordering::Equal => on screen
/// Ordering::Less => above the screen
/// Ordering::Greater => below the screen
pub fn newest_selection_on_screen(&self, cx: &mut AppContext) -> Ordering {
let snapshot = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let newest_head = self
.selections
.newest_anchor()
.head()
.to_display_point(&snapshot);
let screen_top = self
.scroll_manager
.anchor
.anchor
.to_display_point(&snapshot);
// if screen_top > newest_head {
// return Ordering::Less;
// }
if screen_top > newest_head {
return Ordering::Less;
}
// if let Some(visible_lines) = self.visible_line_count() {
// if newest_head.row() < screen_top.row() + visible_lines as u32 {
// return Ordering::Equal;
// }
// }
if let Some(visible_lines) = self.visible_line_count() {
if newest_head.row() < screen_top.row() + visible_lines as u32 {
return Ordering::Equal;
}
}
// Ordering::Greater
// }
Ordering::Greater
}
pub fn read_scroll_position_from_db(
&mut self,

View file

@ -1,148 +1,103 @@
use gpui::AppContext;
use super::Axis;
use crate::{
Autoscroll, Bias, Editor, EditorMode, NextScreen, ScrollAnchor, ScrollCursorBottom,
ScrollCursorCenter, ScrollCursorTop,
};
use gpui::{actions, AppContext, Point, ViewContext};
// actions!(
// editor,
// [
// LineDown,
// LineUp,
// HalfPageDown,
// HalfPageUp,
// PageDown,
// PageUp,
// NextScreen,
// ScrollCursorTop,
// ScrollCursorCenter,
// ScrollCursorBottom,
// ]
// );
impl Editor {
pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) {
if self.take_rename(true, cx).is_some() {
return;
}
pub fn init(cx: &mut AppContext) {
// todo!()
// cx.add_action(Editor::next_screen);
// cx.add_action(Editor::scroll_cursor_top);
// cx.add_action(Editor::scroll_cursor_center);
// cx.add_action(Editor::scroll_cursor_bottom);
// cx.add_action(|this: &mut Editor, _: &LineDown, cx| {
// this.scroll_screen(&ScrollAmount::Line(1.), cx)
// });
// cx.add_action(|this: &mut Editor, _: &LineUp, cx| {
// this.scroll_screen(&ScrollAmount::Line(-1.), cx)
// });
// cx.add_action(|this: &mut Editor, _: &HalfPageDown, cx| {
// this.scroll_screen(&ScrollAmount::Page(0.5), cx)
// });
// cx.add_action(|this: &mut Editor, _: &HalfPageUp, cx| {
// this.scroll_screen(&ScrollAmount::Page(-0.5), cx)
// });
// cx.add_action(|this: &mut Editor, _: &PageDown, cx| {
// this.scroll_screen(&ScrollAmount::Page(1.), cx)
// });
// cx.add_action(|this: &mut Editor, _: &PageUp, cx| {
// this.scroll_screen(&ScrollAmount::Page(-1.), cx)
// });
// todo!()
// if self.mouse_context_menu.read(cx).visible() {
// return None;
// }
if matches!(self.mode, EditorMode::SingleLine) {
cx.propagate();
return;
}
self.request_autoscroll(Autoscroll::Next, cx);
}
pub fn scroll(
&mut self,
scroll_position: Point<f32>,
axis: Option<Axis>,
cx: &mut ViewContext<Self>,
) {
self.scroll_manager.update_ongoing_scroll(axis);
self.set_scroll_position(scroll_position, cx);
}
pub fn scroll_cursor_top(&mut self, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
let snapshot = self.snapshot(cx).display_snapshot;
let scroll_margin_rows = self.vertical_scroll_margin() as u32;
let mut new_screen_top = self.selections.newest_display(cx).head();
*new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
*new_screen_top.column_mut() = 0;
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
self.set_scroll_anchor(
ScrollAnchor {
anchor: new_anchor,
offset: Default::default(),
},
cx,
)
}
pub fn scroll_cursor_center(&mut self, _: &ScrollCursorCenter, cx: &mut ViewContext<Editor>) {
let snapshot = self.snapshot(cx).display_snapshot;
let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
visible_rows as u32
} else {
return;
};
let mut new_screen_top = self.selections.newest_display(cx).head();
*new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
*new_screen_top.column_mut() = 0;
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
self.set_scroll_anchor(
ScrollAnchor {
anchor: new_anchor,
offset: Default::default(),
},
cx,
)
}
pub fn scroll_cursor_bottom(&mut self, _: &ScrollCursorBottom, cx: &mut ViewContext<Editor>) {
let snapshot = self.snapshot(cx).display_snapshot;
let scroll_margin_rows = self.vertical_scroll_margin() as u32;
let visible_rows = if let Some(visible_rows) = self.visible_line_count() {
visible_rows as u32
} else {
return;
};
let mut new_screen_top = self.selections.newest_display(cx).head();
*new_screen_top.row_mut() = new_screen_top
.row()
.saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
*new_screen_top.column_mut() = 0;
let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
self.set_scroll_anchor(
ScrollAnchor {
anchor: new_anchor,
offset: Default::default(),
},
cx,
)
}
}
// impl Editor {
// pub fn next_screen(&mut self, _: &NextScreen, cx: &mut ViewContext<Editor>) -> Option<()> {
// if self.take_rename(true, cx).is_some() {
// return None;
// }
// if self.mouse_context_menu.read(cx).visible() {
// return None;
// }
// if matches!(self.mode, EditorMode::SingleLine) {
// cx.propagate_action();
// return None;
// }
// self.request_autoscroll(Autoscroll::Next, cx);
// Some(())
// }
// pub fn scroll(
// &mut self,
// scroll_position: Vector2F,
// axis: Option<Axis>,
// cx: &mut ViewContext<Self>,
// ) {
// self.scroll_manager.update_ongoing_scroll(axis);
// self.set_scroll_position(scroll_position, cx);
// }
// fn scroll_cursor_top(editor: &mut Editor, _: &ScrollCursorTop, cx: &mut ViewContext<Editor>) {
// let snapshot = editor.snapshot(cx).display_snapshot;
// let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
// let mut new_screen_top = editor.selections.newest_display(cx).head();
// *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(scroll_margin_rows);
// *new_screen_top.column_mut() = 0;
// let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
// let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
// editor.set_scroll_anchor(
// ScrollAnchor {
// anchor: new_anchor,
// offset: Default::default(),
// },
// cx,
// )
// }
// fn scroll_cursor_center(
// editor: &mut Editor,
// _: &ScrollCursorCenter,
// cx: &mut ViewContext<Editor>,
// ) {
// let snapshot = editor.snapshot(cx).display_snapshot;
// let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
// visible_rows as u32
// } else {
// return;
// };
// let mut new_screen_top = editor.selections.newest_display(cx).head();
// *new_screen_top.row_mut() = new_screen_top.row().saturating_sub(visible_rows / 2);
// *new_screen_top.column_mut() = 0;
// let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
// let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
// editor.set_scroll_anchor(
// ScrollAnchor {
// anchor: new_anchor,
// offset: Default::default(),
// },
// cx,
// )
// }
// fn scroll_cursor_bottom(
// editor: &mut Editor,
// _: &ScrollCursorBottom,
// cx: &mut ViewContext<Editor>,
// ) {
// let snapshot = editor.snapshot(cx).display_snapshot;
// let scroll_margin_rows = editor.vertical_scroll_margin() as u32;
// let visible_rows = if let Some(visible_rows) = editor.visible_line_count() {
// visible_rows as u32
// } else {
// return;
// };
// let mut new_screen_top = editor.selections.newest_display(cx).head();
// *new_screen_top.row_mut() = new_screen_top
// .row()
// .saturating_sub(visible_rows.saturating_sub(scroll_margin_rows));
// *new_screen_top.column_mut() = 0;
// let new_screen_top = new_screen_top.to_offset(&snapshot, Bias::Left);
// let new_anchor = snapshot.buffer_snapshot.anchor_before(new_screen_top);
// editor.set_scroll_anchor(
// ScrollAnchor {
// anchor: new_anchor,
// offset: Default::default(),
// },
// cx,
// )
// }
// }

View file

@ -302,39 +302,39 @@ impl SelectionsCollection {
.collect()
}
// pub fn build_columnar_selection(
// &mut self,
// display_map: &DisplaySnapshot,
// row: u32,
// positions: &Range<Pixels>,
// reversed: bool,
// text_layout_details: &TextLayoutDetails,
// ) -> Option<Selection<Point>> {
// let is_empty = positions.start == positions.end;
// let line_len = display_map.line_len(row);
pub fn build_columnar_selection(
&mut self,
display_map: &DisplaySnapshot,
row: u32,
positions: &Range<Pixels>,
reversed: bool,
text_layout_details: &TextLayoutDetails,
) -> Option<Selection<Point>> {
let is_empty = positions.start == positions.end;
let line_len = display_map.line_len(row);
// let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details);
let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details);
// let start_col = layed_out_line.closest_index_for_x(positions.start) as u32;
// if start_col < line_len || (is_empty && positions.start == layed_out_line.width()) {
// let start = DisplayPoint::new(row, start_col);
// let end_col = layed_out_line.closest_index_for_x(positions.end) as u32;
// let end = DisplayPoint::new(row, end_col);
let start_col = layed_out_line.closest_index_for_x(positions.start) as u32;
if start_col < line_len || (is_empty && positions.start == layed_out_line.width) {
let start = DisplayPoint::new(row, start_col);
let end_col = layed_out_line.closest_index_for_x(positions.end) as u32;
let end = DisplayPoint::new(row, end_col);
// Some(Selection {
// id: post_inc(&mut self.next_selection_id),
// start: start.to_point(display_map),
// end: end.to_point(display_map),
// reversed,
// goal: SelectionGoal::HorizontalRange {
// start: positions.start,
// end: positions.end,
// },
// })
// } else {
// None
// }
// }
Some(Selection {
id: post_inc(&mut self.next_selection_id),
start: start.to_point(display_map),
end: end.to_point(display_map),
reversed,
goal: SelectionGoal::HorizontalRange {
start: positions.start.into(),
end: positions.end.into(),
},
})
} else {
None
}
}
pub(crate) fn change_with<R>(
&mut self,

View file

@ -0,0 +1,25 @@
[package]
name = "go_to_line2"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/go_to_line.rs"
doctest = false
[dependencies]
editor = { package = "editor2", path = "../editor2" }
gpui = { package = "gpui2", path = "../gpui2" }
menu = { package = "menu2", path = "../menu2" }
serde.workspace = true
settings = { package = "settings2", path = "../settings2" }
text = { package = "text2", path = "../text2" }
workspace = { package = "workspace2", path = "../workspace2" }
postage.workspace = true
theme = { package = "theme2", path = "../theme2" }
ui = { package = "ui2", path = "../ui2" }
util = { path = "../util" }
[dev-dependencies]
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }

View file

@ -0,0 +1,221 @@
use gpui::{actions, div, px, red, AppContext, Div, Render, Styled, ViewContext, VisualContext};
use workspace::ModalRegistry;
actions!(Toggle);
pub fn init(cx: &mut AppContext) {
cx.global_mut::<ModalRegistry>()
.register_modal(Toggle, |_, cx| {
// if let Some(editor) = workspace
// .active_item(cx)
// .and_then(|active_item| active_item.downcast::<Editor>())
// {
// cx.build_view(|cx| GoToLine::new(editor, cx))
// }
let view = cx.build_view(|_| GoToLine);
view
});
// cx.add_action(GoToLine::toggle);
// cx.add_action(GoToLine::confirm);
// cx.add_action(GoToLine::cancel);
}
pub struct GoToLine;
impl Render for GoToLine {
type Element = Div<Self>;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
dbg!("rendering GoToLine");
div().bg(red()).w(px(100.0)).h(px(100.0))
}
}
// pub struct GoToLine {
// //line_editor: View<Editor>,
// active_editor: View<Editor>,
// prev_scroll_position: Option<gpui::Point<Pixels>>,
// cursor_point: Point,
// max_point: Point,
// has_focus: bool,
// }
// pub enum Event {
// Dismissed,
// }
// impl GoToLine {
// pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
// // let line_editor = cx.build_view(|cx| {
// // Editor::single_line(
// // Some(Arc::new(|theme| theme.picker.input_editor.clone())),
// // cx,
// // )
// // });
// // cx.subscribe(&line_editor, Self::on_line_editor_event)
// // .detach();
// let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
// let scroll_position = editor.scroll_position(cx);
// let buffer = editor.buffer().read(cx).snapshot(cx);
// (
// Some(scroll_position),
// editor.selections.newest(cx).head(),
// buffer.max_point(),
// )
// });
// cx.on_release(|_, on_release| {}).detach();
// Self {
// //line_editor,
// active_editor,
// prev_scroll_position: scroll_position,
// cursor_point,
// max_point,
// has_focus: false,
// }
// }
// fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
// cx.emit(Event::Dismissed);
// }
// fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
// self.prev_scroll_position.take();
// if let Some(point) = self.point_from_query(cx) {
// self.active_editor.update(cx, |active_editor, cx| {
// let snapshot = active_editor.snapshot(cx).display_snapshot;
// let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
// active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
// s.select_ranges([point..point])
// });
// });
// }
// cx.emit(Event::Dismissed);
// }
// fn on_line_editor_event(
// &mut self,
// _: View<Editor>,
// event: &editor::Event,
// cx: &mut ViewContext<Self>,
// ) {
// match event {
// editor::Event::Blurred => cx.emit(Event::Dismissed),
// editor::Event::BufferEdited { .. } => {
// if let Some(point) = self.point_from_query(cx) {
// // todo!()
// // self.active_editor.update(cx, |active_editor, cx| {
// // let snapshot = active_editor.snapshot(cx).display_snapshot;
// // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
// // let display_point = point.to_display_point(&snapshot);
// // let row = display_point.row();
// // active_editor.highlight_rows(Some(row..row + 1));
// // active_editor.request_autoscroll(Autoscroll::center(), cx);
// // });
// cx.notify();
// }
// }
// _ => {}
// }
// }
// fn point_from_query(&self, cx: &ViewContext<Self>) -> Option<Point> {
// return None;
// // todo!()
// // let line_editor = self.line_editor.read(cx).text(cx);
// // let mut components = line_editor
// // .splitn(2, FILE_ROW_COLUMN_DELIMITER)
// // .map(str::trim)
// // .fuse();
// // let row = components.next().and_then(|row| row.parse::<u32>().ok())?;
// // let column = components.next().and_then(|col| col.parse::<u32>().ok());
// // Some(Point::new(
// // row.saturating_sub(1),
// // column.unwrap_or(0).saturating_sub(1),
// // ))
// }
// }
// impl EventEmitter for GoToLine {
// type Event = Event;
// }
// impl Entity for GoToLine {
// fn release(&mut self, cx: &mut AppContext) {
// let scroll_position = self.prev_scroll_position.take();
// self.active_editor.window().update(cx, |cx| {
// self.active_editor.update(cx, |editor, cx| {
// editor.highlight_rows(None);
// if let Some(scroll_position) = scroll_position {
// editor.set_scroll_position(scroll_position, cx);
// }
// })
// });
// }
// }
// impl Render for GoToLine {
// type Element = Div<Self>;
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
// // todo!()
// div()
// }
// }
// impl View for GoToLine {
// fn ui_name() -> &'static str {
// "GoToLine"
// }
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
// let theme = &theme::current(cx).picker;
// let label = format!(
// "{}{FILE_ROW_COLUMN_DELIMITER}{} of {} lines",
// self.cursor_point.row + 1,
// self.cursor_point.column + 1,
// self.max_point.row + 1
// );
// Flex::new(Axis::Vertical)
// .with_child(
// ChildView::new(&self.line_editor, cx)
// .contained()
// .with_style(theme.input_editor.container),
// )
// .with_child(
// Label::new(label, theme.no_matches.label.clone())
// .contained()
// .with_style(theme.no_matches.container),
// )
// .contained()
// .with_style(theme.container)
// .constrained()
// .with_max_width(500.0)
// .into_any_named("go to line")
// }
// fn focus_in(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
// self.has_focus = true;
// cx.focus(&self.line_editor);
// }
// fn focus_out(&mut self, _: AnyView, _: &mut ViewContext<Self>) {
// self.has_focus = false;
// }
// }
// impl Modal for GoToLine {
// fn has_focus(&self) -> bool {
// self.has_focus
// }
// fn dismiss_on_event(event: &Self::Event) -> bool {
// matches!(event, Event::Dismissed)
// }
// }

View file

@ -1,10 +1,55 @@
use crate::SharedString;
use anyhow::{anyhow, Context, Result};
use collections::{HashMap, HashSet};
use lazy_static::lazy_static;
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use serde::Deserialize;
use std::any::{type_name, Any};
pub trait Action: 'static {
/// Actions are used to implement keyboard-driven UI.
/// When you declare an action, you can bind keys to the action in the keymap and
/// listeners for that action in the element tree.
///
/// To declare a list of simple actions, you can use the actions! macro, which defines a simple unit struct
/// action for each listed action name.
/// ```rust
/// actions!(MoveUp, MoveDown, MoveLeft, MoveRight, Newline);
/// ```
/// More complex data types can also be actions. If you annotate your type with the `#[action]` proc macro,
/// it will automatically
/// ```
/// #[action]
/// pub struct SelectNext {
/// pub replace_newest: bool,
/// }
///
/// Any type A that satisfies the following bounds is automatically an action:
///
/// ```
/// A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static,
/// ```
///
/// The `#[action]` annotation will derive these implementations for your struct automatically. If you
/// want to control them manually, you can use the lower-level `#[register_action]` macro, which only
/// generates the code needed to register your action before `main`. Then you'll need to implement all
/// the traits manually.
///
/// ```
/// #[gpui::register_action]
/// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::fmt::Debug)]
/// pub struct Paste {
/// pub content: SharedString,
/// }
///
/// impl std::default::Default for Paste {
/// fn default() -> Self {
/// Self {
/// content: SharedString::from("🍝"),
/// }
/// }
/// }
/// ```
pub trait Action: std::fmt::Debug + 'static {
fn qualified_name() -> SharedString
where
Self: Sized;
@ -17,12 +62,14 @@ pub trait Action: 'static {
fn as_any(&self) -> &dyn Any;
}
// Types become actions by satisfying a list of trait bounds.
impl<A> Action for A
where
A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + 'static,
A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static,
{
fn qualified_name() -> SharedString {
type_name::<A>().into()
// todo!() remove the 2 replacement when migration is done
type_name::<A>().replace("2::", "::").into()
}
fn build(params: Option<serde_json::Value>) -> Result<Box<dyn Action>>
@ -53,6 +100,61 @@ where
}
}
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
lazy_static! {
static ref ACTION_REGISTRY: RwLock<ActionRegistry> = RwLock::default();
}
#[derive(Default)]
struct ActionRegistry {
builders_by_name: HashMap<SharedString, ActionBuilder>,
all_names: Vec<SharedString>, // So we can return a static slice.
}
/// Register an action type to allow it to be referenced in keymaps.
pub fn register_action<A: Action>() {
let name = A::qualified_name();
let mut lock = ACTION_REGISTRY.write();
lock.builders_by_name.insert(name.clone(), A::build);
lock.all_names.push(name);
}
/// Construct an action based on its name and optional JSON parameters sourced from the keymap.
pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> {
let lock = ACTION_REGISTRY.read();
let build_action = lock
.builders_by_name
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build_action)(params)
}
pub fn all_action_names() -> MappedRwLockReadGuard<'static, [SharedString]> {
let lock = ACTION_REGISTRY.read();
RwLockReadGuard::map(lock, |registry: &ActionRegistry| {
registry.all_names.as_slice()
})
}
/// Defines unit structs that can be used as actions.
/// To use more complex data types as actions, annotate your type with the #[action] macro.
#[macro_export]
macro_rules! actions {
() => {};
( $name:ident ) => {
#[gpui::register_action]
#[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)]
pub struct $name;
};
( $name:ident, $($rest:tt)* ) => {
actions!($name);
actions!($($rest)*);
};
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DispatchContext {
set: HashSet<SharedString>,
@ -290,8 +392,28 @@ fn skip_whitespace(source: &str) -> &str {
#[cfg(test)]
mod tests {
use super::*;
use crate as gpui;
use DispatchContextPredicate::*;
#[test]
fn test_actions_definition() {
{
actions!(A, B, C, D, E, F, G);
}
{
actions!(
A,
B,
C,
D,
E,
F,
G, // Don't wrap, test the trailing comma
);
}
}
#[test]
fn test_parse_context() {
let mut expected = DispatchContext::default();

View file

@ -17,9 +17,9 @@ use crate::{
current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle,
AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, EventEmitter, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap,
LayoutId, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SharedString,
SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem,
View, Window, WindowContext, WindowHandle, WindowId,
LayoutId, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet,
Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@ -144,7 +144,6 @@ impl App {
}
}
type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
@ -158,7 +157,7 @@ type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
// }
pub struct AppContext {
this: Weak<AppCell>,
pub(crate) this: Weak<AppCell>,
pub(crate) platform: Rc<dyn Platform>,
app_metadata: AppMetadata,
text_system: Arc<TextSystem>,
@ -180,7 +179,6 @@ pub struct AppContext {
pub(crate) keymap: Arc<Mutex<Keymap>>,
pub(crate) global_action_listeners:
HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>,
action_builders: HashMap<SharedString, ActionBuilder>,
pending_effects: VecDeque<Effect>,
pub(crate) pending_notifications: HashSet<EntityId>,
pub(crate) pending_global_notifications: HashSet<TypeId>,
@ -238,7 +236,6 @@ impl AppContext {
windows: SlotMap::with_key(),
keymap: Arc::new(Mutex::new(Keymap::default())),
global_action_listeners: HashMap::default(),
action_builders: HashMap::default(),
pending_effects: VecDeque::new(),
pending_notifications: HashSet::default(),
pending_global_notifications: HashSet::default(),
@ -755,13 +752,17 @@ impl AppContext {
self.globals_by_type.insert(global_type, Box::new(global));
}
#[track_caller]
pub fn clear_globals(&mut self) {
dbg!(core::panic::Location::caller());
//todo!(notify globals?)
self.globals_by_type.drain();
}
/// Set the value of the global of the given type.
#[track_caller]
pub fn remove_global<G: Any>(&mut self) -> G {
dbg!(core::panic::Location::caller());
let global_type = TypeId::of::<G>();
//todo!(notify globals?)
*self
@ -795,10 +796,6 @@ impl AppContext {
)
}
pub fn all_action_names<'a>(&'a self) -> impl Iterator<Item = SharedString> + 'a {
self.action_builders.keys().cloned()
}
/// Move the global of the given type to the stack.
pub(crate) fn lease_global<G: 'static>(&mut self) -> GlobalLease<G> {
GlobalLease::new(
@ -861,29 +858,21 @@ impl AppContext {
}));
}
/// Register an action type to allow it to be referenced in keymaps.
pub fn register_action_type<A: Action>(&mut self) {
self.action_builders.insert(A::qualified_name(), A::build);
}
/// Construct an action based on its name and parameters.
pub fn build_action(
&mut self,
name: &str,
params: Option<serde_json::Value>,
) -> Result<Box<dyn Action>> {
let build = self
.action_builders
.get(name)
.ok_or_else(|| anyhow!("no action type registered for {}", name))?;
(build)(params)
}
/// Halt propagation of a mouse event, keyboard event, or action. This prevents listeners
/// that have not yet been invoked from receiving the event.
/// Event handlers propagate events by default. Call this method to stop dispatching to
/// event handlers with a lower z-index (mouse) or higher in the tree (keyboard). This is
/// the opposite of [propagate]. It's also possible to cancel a call to [propagate] by
/// calling this method before effects are flushed.
pub fn stop_propagation(&mut self) {
self.propagate_event = false;
}
/// Action handlers stop propagation by default during the bubble phase of action dispatch
/// dispatching to action handlers higher in the element tree. This is the opposite of
/// [stop_propagation]. It's also possible to cancel a call to [stop_propagate] by calling
/// this method before effects are flushed.
pub fn propagate(&mut self) {
self.propagate_event = true;
}
}
impl Context for AppContext {

View file

@ -13,6 +13,7 @@ use std::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak,
},
thread::panicking,
};
slotmap::new_key_type! { pub struct EntityId; }
@ -140,9 +141,8 @@ impl<'a, T: 'static> core::ops::DerefMut for Lease<'a, T> {
impl<'a, T> Drop for Lease<'a, T> {
fn drop(&mut self) {
if self.entity.is_some() {
// We don't panic here, because other panics can cause us to drop the lease without ending it cleanly.
log::error!("Leases must be ended with EntityMap::end_lease")
if self.entity.is_some() && !panicking() {
panic!("Leases must be ended with EntityMap::end_lease")
}
}
}

View file

@ -1,7 +1,8 @@
use crate::{
AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, BackgroundExecutor, Context,
EventEmitter, ForegroundExecutor, Model, ModelContext, Render, Result, Task, TestDispatcher,
TestPlatform, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model, ModelContext,
Render, Result, Task, TestDispatcher, TestPlatform, ViewContext, VisualContext, WindowContext,
WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt};
@ -161,6 +162,23 @@ impl TestAppContext {
}
}
pub fn dispatch_keystroke(
&mut self,
window: AnyWindowHandle,
keystroke: Keystroke,
is_held: bool,
) {
let handled = window
.update(self, |_, cx| {
cx.dispatch_event(InputEvent::KeyDown(KeyDownEvent { keystroke, is_held }))
})
.is_ok_and(|handled| handled);
if !handled {
// todo!() simluate input here
}
}
pub fn notifications<T: 'static>(&mut self, entity: &Model<T>) -> impl Stream<Item = ()> {
let (tx, rx) = futures::channel::mpsc::unbounded();

View file

@ -176,6 +176,15 @@ pub fn black() -> Hsla {
}
}
pub fn transparent_black() -> Hsla {
Hsla {
h: 0.,
s: 0.,
l: 0.,
a: 0.,
}
}
pub fn white() -> Hsla {
Hsla {
h: 0.,
@ -194,6 +203,15 @@ pub fn red() -> Hsla {
}
}
pub fn blue() -> Hsla {
Hsla {
h: 0.6,
s: 1.,
l: 0.5,
a: 1.,
}
}
impl Hsla {
/// Returns true if the HSLA color is fully transparent, false otherwise.
pub fn is_transparent(&self) -> bool {

View file

@ -134,7 +134,10 @@ where
.layout(state, frame_state.as_mut().unwrap(), cx);
}
}
_ => panic!("must call initialize before layout"),
ElementRenderPhase::Start => panic!("must call initialize before layout"),
ElementRenderPhase::LayoutRequested { .. } | ElementRenderPhase::Painted => {
panic!("element rendered twice")
}
};
self.phase = ElementRenderPhase::LayoutRequested {

View file

@ -2,8 +2,10 @@ mod div;
mod img;
mod svg;
mod text;
mod uniform_list;
pub use div::*;
pub use img::*;
pub use svg::*;
pub use text::*;
pub use uniform_list::*;

View file

@ -1,28 +1,28 @@
use crate::{
point, AnyElement, BorrowWindow, Bounds, Component, Element, ElementFocus, ElementId,
ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable,
ElementInteractivity, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable,
GlobalElementId, GroupBounds, InteractiveElementState, LayoutId, Overflow, ParentElement,
Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, Visibility,
Pixels, Point, SharedString, StatefulInteractive, StatefulInteractivity, StatelessInteractive,
StatelessInteractivity, Style, StyleRefinement, Styled, ViewContext, Visibility,
};
use refineable::Refineable;
use smallvec::SmallVec;
pub struct Div<
V: 'static,
I: ElementInteraction<V> = StatelessInteraction<V>,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: ElementFocus<V> = FocusDisabled,
> {
interaction: I,
interactivity: I,
focus: F,
children: SmallVec<[AnyElement<V>; 2]>,
group: Option<SharedString>,
base_style: StyleRefinement,
}
pub fn div<V: 'static>() -> Div<V, StatelessInteraction<V>, FocusDisabled> {
pub fn div<V: 'static>() -> Div<V, StatelessInteractivity<V>, FocusDisabled> {
Div {
interaction: StatelessInteraction::default(),
interactivity: StatelessInteractivity::default(),
focus: FocusDisabled,
children: SmallVec::new(),
group: None,
@ -30,14 +30,14 @@ pub fn div<V: 'static>() -> Div<V, StatelessInteraction<V>, FocusDisabled> {
}
}
impl<V, F> Div<V, StatelessInteraction<V>, F>
impl<V, F> Div<V, StatelessInteractivity<V>, F>
where
V: 'static,
F: ElementFocus<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Div<V, StatefulInteraction<V>, F> {
pub fn id(self, id: impl Into<ElementId>) -> Div<V, StatefulInteractivity<V>, F> {
Div {
interaction: id.into().into(),
interactivity: id.into().into(),
focus: self.focus,
children: self.children,
group: self.group,
@ -48,7 +48,7 @@ where
impl<V, I, F> Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
pub fn group(mut self, group: impl Into<SharedString>) -> Self {
@ -98,16 +98,20 @@ where
let mut computed_style = Style::default();
computed_style.refine(&self.base_style);
self.focus.refine_style(&mut computed_style, cx);
self.interaction
.refine_style(&mut computed_style, bounds, &element_state.interactive, cx);
self.interactivity.refine_style(
&mut computed_style,
bounds,
&element_state.interactive,
cx,
);
computed_style
}
}
impl<V: 'static> Div<V, StatefulInteraction<V>, FocusDisabled> {
pub fn focusable(self) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
impl<V: 'static> Div<V, StatefulInteractivity<V>, FocusDisabled> {
pub fn focusable(self) -> Div<V, StatefulInteractivity<V>, FocusEnabled<V>> {
Div {
interaction: self.interaction,
interactivity: self.interactivity,
focus: FocusEnabled::new(),
children: self.children,
group: self.group,
@ -118,9 +122,9 @@ impl<V: 'static> Div<V, StatefulInteraction<V>, FocusDisabled> {
pub fn track_focus(
self,
handle: &FocusHandle,
) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
) -> Div<V, StatefulInteractivity<V>, FocusEnabled<V>> {
Div {
interaction: self.interaction,
interactivity: self.interactivity,
focus: FocusEnabled::tracked(handle),
children: self.children,
group: self.group,
@ -145,13 +149,13 @@ impl<V: 'static> Div<V, StatefulInteraction<V>, FocusDisabled> {
}
}
impl<V: 'static> Div<V, StatelessInteraction<V>, FocusDisabled> {
impl<V: 'static> Div<V, StatelessInteractivity<V>, FocusDisabled> {
pub fn track_focus(
self,
handle: &FocusHandle,
) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
) -> Div<V, StatefulInteractivity<V>, FocusEnabled<V>> {
Div {
interaction: self.interaction.into_stateful(handle),
interactivity: self.interactivity.into_stateful(handle),
focus: handle.clone().into(),
children: self.children,
group: self.group,
@ -163,7 +167,7 @@ impl<V: 'static> Div<V, StatelessInteraction<V>, FocusDisabled> {
impl<V, I> Focusable<V> for Div<V, I, FocusEnabled<V>>
where
V: 'static,
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
&mut self.focus.focus_listeners
@ -191,13 +195,13 @@ pub struct DivState {
impl<V, I, F> Element<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
type ElementState = DivState;
fn id(&self) -> Option<ElementId> {
self.interaction
self.interactivity
.as_stateful()
.map(|identified| identified.id.clone())
}
@ -209,15 +213,15 @@ where
cx: &mut ViewContext<V>,
) -> Self::ElementState {
let mut element_state = element_state.unwrap_or_default();
self.focus
.initialize(element_state.focus_handle.take(), cx, |focus_handle, cx| {
element_state.focus_handle = focus_handle;
self.interaction.initialize(cx, |cx| {
self.interactivity.initialize(cx, |cx| {
self.focus
.initialize(element_state.focus_handle.take(), cx, |focus_handle, cx| {
element_state.focus_handle = focus_handle;
for child in &mut self.children {
child.initialize(view_state, cx);
}
})
});
});
element_state
}
@ -281,11 +285,11 @@ where
(child_max - child_min).into()
};
cx.stack(z_index, |cx| {
cx.stack(0, |cx| {
cx.with_z_index(z_index, |cx| {
cx.with_z_index(0, |cx| {
style.paint(bounds, cx);
this.focus.paint(bounds, cx);
this.interaction.paint(
this.interactivity.paint(
bounds,
content_size,
style.overflow,
@ -293,7 +297,7 @@ where
cx,
);
});
cx.stack(1, |cx| {
cx.with_z_index(1, |cx| {
style.apply_text_style(cx, |cx| {
style.apply_overflow(bounds, cx, |cx| {
let scroll_offset = element_state.interactive.scroll_offset();
@ -316,7 +320,7 @@ where
impl<V, I, F> Component<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn render(self) -> AnyElement<V> {
@ -326,7 +330,7 @@ where
impl<V, I, F> ParentElement<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
@ -336,7 +340,7 @@ where
impl<V, I, F> Styled for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn style(&mut self) -> &mut StyleRefinement {
@ -346,19 +350,19 @@ where
impl<V, I, F> StatelessInteractive<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V> {
self.interaction.as_stateless_mut()
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.interactivity.as_stateless_mut()
}
}
impl<V, F> StatefulInteractive<V> for Div<V, StatefulInteraction<V>, F>
impl<V, F> StatefulInteractive<V> for Div<V, StatefulInteractivity<V>, F>
where
F: ElementFocus<V>,
{
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V> {
&mut self.interaction
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
&mut self.interactivity
}
}

View file

@ -1,15 +1,15 @@
use crate::{
div, AnyElement, BorrowWindow, Bounds, Component, Div, DivState, Element, ElementFocus,
ElementId, ElementInteraction, FocusDisabled, FocusEnabled, FocusListeners, Focusable,
LayoutId, Pixels, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
StatelessInteractive, StyleRefinement, Styled, ViewContext,
ElementId, ElementInteractivity, FocusDisabled, FocusEnabled, FocusListeners, Focusable,
LayoutId, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
};
use futures::FutureExt;
use util::ResultExt;
pub struct Img<
V: 'static,
I: ElementInteraction<V> = StatelessInteraction<V>,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: ElementFocus<V> = FocusDisabled,
> {
base: Div<V, I, F>,
@ -17,7 +17,7 @@ pub struct Img<
grayscale: bool,
}
pub fn img<V: 'static>() -> Img<V, StatelessInteraction<V>, FocusDisabled> {
pub fn img<V: 'static>() -> Img<V, StatelessInteractivity<V>, FocusDisabled> {
Img {
base: div(),
uri: None,
@ -28,7 +28,7 @@ pub fn img<V: 'static>() -> Img<V, StatelessInteraction<V>, FocusDisabled> {
impl<V, I, F> Img<V, I, F>
where
V: 'static,
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
@ -42,11 +42,11 @@ where
}
}
impl<V, F> Img<V, StatelessInteraction<V>, F>
impl<V, F> Img<V, StatelessInteractivity<V>, F>
where
F: ElementFocus<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteraction<V>, F> {
pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteractivity<V>, F> {
Img {
base: self.base.id(id),
uri: self.uri,
@ -57,7 +57,7 @@ where
impl<V, I, F> Component<V> for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn render(self) -> AnyElement<V> {
@ -67,7 +67,7 @@ where
impl<V, I, F> Element<V> for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
type ElementState = DivState;
@ -101,7 +101,7 @@ where
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
cx.stack(0, |cx| {
cx.with_z_index(0, |cx| {
self.base.paint(bounds, view, element_state, cx);
});
@ -118,7 +118,7 @@ where
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.stack(1, |cx| {
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
@ -136,7 +136,7 @@ where
impl<V, I, F> Styled for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn style(&mut self) -> &mut StyleRefinement {
@ -146,27 +146,27 @@ where
impl<V, I, F> StatelessInteractive<V> for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V> {
self.base.stateless_interaction()
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteraction<V>, F>
impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteractivity<V>, F>
where
F: ElementFocus<V>,
{
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V> {
self.base.stateful_interaction()
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V, I> Focusable<V> for Img<V, I, FocusEnabled<V>>
where
V: 'static,
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()

View file

@ -1,21 +1,21 @@
use crate::{
div, AnyElement, Bounds, Component, Div, DivState, Element, ElementFocus, ElementId,
ElementInteraction, FocusDisabled, FocusEnabled, FocusListeners, Focusable, LayoutId, Pixels,
SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
StatelessInteractive, StyleRefinement, Styled, ViewContext,
ElementInteractivity, FocusDisabled, FocusEnabled, FocusListeners, Focusable, LayoutId, Pixels,
SharedString, StatefulInteractive, StatefulInteractivity, StatelessInteractive,
StatelessInteractivity, StyleRefinement, Styled, ViewContext,
};
use util::ResultExt;
pub struct Svg<
V: 'static,
I: ElementInteraction<V> = StatelessInteraction<V>,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: ElementFocus<V> = FocusDisabled,
> {
base: Div<V, I, F>,
path: Option<SharedString>,
}
pub fn svg<V: 'static>() -> Svg<V, StatelessInteraction<V>, FocusDisabled> {
pub fn svg<V: 'static>() -> Svg<V, StatelessInteractivity<V>, FocusDisabled> {
Svg {
base: div(),
path: None,
@ -24,7 +24,7 @@ pub fn svg<V: 'static>() -> Svg<V, StatelessInteraction<V>, FocusDisabled> {
impl<V, I, F> Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
@ -33,11 +33,11 @@ where
}
}
impl<V, F> Svg<V, StatelessInteraction<V>, F>
impl<V, F> Svg<V, StatelessInteractivity<V>, F>
where
F: ElementFocus<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteraction<V>, F> {
pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteractivity<V>, F> {
Svg {
base: self.base.id(id),
path: self.path,
@ -47,7 +47,7 @@ where
impl<V, I, F> Component<V> for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn render(self) -> AnyElement<V> {
@ -57,7 +57,7 @@ where
impl<V, I, F> Element<V> for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
type ElementState = DivState;
@ -107,7 +107,7 @@ where
impl<V, I, F> Styled for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn style(&mut self) -> &mut StyleRefinement {
@ -117,27 +117,27 @@ where
impl<V, I, F> StatelessInteractive<V> for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V> {
self.base.stateless_interaction()
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteraction<V>, F>
impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteractivity<V>, F>
where
V: 'static,
F: ElementFocus<V>,
{
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V> {
self.base.stateful_interaction()
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V: 'static, I> Focusable<V> for Svg<V, I, FocusEnabled<V>>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()

View file

@ -74,7 +74,7 @@ impl<V: 'static> Element<V> for Text<V> {
) -> LayoutId {
let text_system = cx.text_system().clone();
let text_style = cx.text_style();
let font_size = text_style.font_size * cx.rem_size();
let font_size = text_style.font_size.to_pixels(cx.rem_size());
let line_height = text_style
.line_height
.to_pixels(font_size.into(), cx.rem_size());
@ -127,6 +127,7 @@ impl<V: 'static> Element<V> for Text<V> {
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
let line_height = element_state.line_height;
let mut line_origin = bounds.origin;
for line in &element_state.lines {

View file

@ -0,0 +1,244 @@
use crate::{
point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
StyleRefinement, Styled, ViewContext,
};
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{cmp, ops::Range, sync::Arc};
use taffy::style::Overflow;
pub fn uniform_list<Id, V, C>(
id: Id,
item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[C; 64]>,
) -> UniformList<V>
where
Id: Into<ElementId>,
V: 'static,
C: Component<V>,
{
let id = id.into();
UniformList {
id: id.clone(),
style: Default::default(),
item_count,
render_items: Box::new(move |view, visible_range, cx| {
f(view, visible_range, cx)
.into_iter()
.map(|component| component.render())
.collect()
}),
interactivity: id.into(),
scroll_handle: None,
}
}
pub struct UniformList<V: 'static> {
id: ElementId,
style: StyleRefinement,
item_count: usize,
render_items: Box<
dyn for<'a> Fn(
&'a mut V,
Range<usize>,
&'a mut ViewContext<V>,
) -> SmallVec<[AnyElement<V>; 64]>,
>,
interactivity: StatefulInteractivity<V>,
scroll_handle: Option<UniformListScrollHandle>,
}
#[derive(Clone)]
pub struct UniformListScrollHandle(Arc<Mutex<Option<ScrollHandleState>>>);
#[derive(Clone, Debug)]
struct ScrollHandleState {
item_height: Pixels,
list_height: Pixels,
scroll_offset: Arc<Mutex<Point<Pixels>>>,
}
impl UniformListScrollHandle {
pub fn new() -> Self {
Self(Arc::new(Mutex::new(None)))
}
pub fn scroll_to_item(&self, ix: usize) {
if let Some(state) = &*self.0.lock() {
let mut scroll_offset = state.scroll_offset.lock();
let item_top = state.item_height * ix;
let item_bottom = item_top + state.item_height;
let scroll_top = -scroll_offset.y;
if item_top < scroll_top {
scroll_offset.y = -item_top;
} else if item_bottom > scroll_top + state.list_height {
scroll_offset.y = -(item_bottom - state.list_height);
}
}
}
}
impl<V: 'static> Styled for UniformList<V> {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl<V: 'static> Element<V> for UniformList<V> {
type ElementState = InteractiveElementState;
fn id(&self) -> Option<crate::ElementId> {
Some(self.id.clone())
}
fn initialize(
&mut self,
_: &mut V,
element_state: Option<Self::ElementState>,
_: &mut ViewContext<V>,
) -> Self::ElementState {
element_state.unwrap_or_default()
}
fn layout(
&mut self,
_view_state: &mut V,
_element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
cx.request_layout(&self.computed_style(), None)
}
fn paint(
&mut self,
bounds: crate::Bounds<crate::Pixels>,
view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
let style = self.computed_style();
style.paint(bounds, cx);
let border = style.border_widths.to_pixels(cx.rem_size());
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
let padded_bounds = Bounds::from_corners(
bounds.origin + point(border.left + padding.left, border.top + padding.top),
bounds.lower_right()
- point(border.right + padding.right, border.bottom + padding.bottom),
);
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
let content_size;
if self.item_count > 0 {
let item_height = self.measure_item_height(view_state, padded_bounds, cx);
if let Some(scroll_handle) = self.scroll_handle.clone() {
scroll_handle.0.lock().replace(ScrollHandleState {
item_height,
list_height: padded_bounds.size.height,
scroll_offset: element_state.track_scroll_offset(),
});
}
let visible_item_count = if item_height > px(0.) {
(padded_bounds.size.height / item_height).ceil() as usize + 1
} else {
0
};
let scroll_offset = element_state
.scroll_offset()
.map_or((0.0).into(), |offset| offset.y);
let first_visible_element_ix = (-scroll_offset / item_height).floor() as usize;
let visible_range = first_visible_element_ix
..cmp::min(
first_visible_element_ix + visible_item_count,
self.item_count,
);
let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
content_size = Size {
width: padded_bounds.size.width,
height: item_height * self.item_count,
};
cx.with_z_index(1, |cx| {
for (item, ix) in items.iter_mut().zip(visible_range) {
item.initialize(view_state, cx);
let layout_id = item.layout(view_state, cx);
cx.compute_layout(
layout_id,
Size {
width: AvailableSpace::Definite(bounds.size.width),
height: AvailableSpace::Definite(item_height),
},
);
let offset =
padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset);
cx.with_element_offset(Some(offset), |cx| item.paint(view_state, cx))
}
});
} else {
content_size = Size {
width: bounds.size.width,
height: px(0.),
};
}
let overflow = point(style.overflow.x, Overflow::Scroll);
cx.with_z_index(0, |cx| {
self.interactivity
.paint(bounds, content_size, overflow, element_state, cx);
});
})
}
}
impl<V> UniformList<V> {
fn measure_item_height(
&self,
view_state: &mut V,
list_bounds: Bounds<Pixels>,
cx: &mut ViewContext<V>,
) -> Pixels {
let mut items = (self.render_items)(view_state, 0..1, cx);
debug_assert!(items.len() == 1);
let mut item_to_measure = items.pop().unwrap();
item_to_measure.initialize(view_state, cx);
let layout_id = item_to_measure.layout(view_state, cx);
cx.compute_layout(
layout_id,
Size {
width: AvailableSpace::Definite(list_bounds.size.width),
height: AvailableSpace::MinContent,
},
);
cx.layout_bounds(layout_id).size.height
}
pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
self.scroll_handle = Some(handle);
self
}
}
impl<V: 'static> StatelessInteractive<V> for UniformList<V> {
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.interactivity.as_stateless_mut()
}
}
impl<V: 'static> StatefulInteractive<V> for UniformList<V> {
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
&mut self.interactivity
}
}
impl<V: 'static> Component<V> for UniformList<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
}
}

View file

@ -71,7 +71,8 @@ impl<T> Future for Task<T> {
}
}
}
type AnyLocalFuture<R> = Pin<Box<dyn 'static + Future<Output = R>>>;
type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
impl BackgroundExecutor {
pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
Self { dispatcher }
@ -84,10 +85,16 @@ impl BackgroundExecutor {
R: Send + 'static,
{
let dispatcher = self.dispatcher.clone();
let (runnable, task) =
async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
runnable.schedule();
Task::Spawned(task)
fn inner<R: Send + 'static>(
dispatcher: Arc<dyn PlatformDispatcher>,
future: AnyFuture<R>,
) -> Task<R> {
let (runnable, task) =
async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
runnable.schedule();
Task::Spawned(task)
}
inner::<R>(dispatcher, Box::pin(future))
}
#[cfg(any(test, feature = "test-support"))]
@ -251,11 +258,17 @@ impl ForegroundExecutor {
R: 'static,
{
let dispatcher = self.dispatcher.clone();
let (runnable, task) = async_task::spawn_local(future, move |runnable| {
dispatcher.dispatch_on_main_thread(runnable)
});
runnable.schedule();
Task::Spawned(task)
fn inner<R: 'static>(
dispatcher: Arc<dyn PlatformDispatcher>,
future: AnyLocalFuture<R>,
) -> Task<R> {
let (runnable, task) = async_task::spawn_local(future, move |runnable| {
dispatcher.dispatch_on_main_thread(runnable)
});
runnable.schedule();
Task::Spawned(task)
}
inner::<R>(dispatcher, Box::pin(future))
}
}

View file

@ -25,6 +25,10 @@ impl<T: Clone + Debug + Default> Point<T> {
Self { x, y }
}
pub fn zero() -> Self {
Self::new(T::default(), T::default())
}
pub fn map<U: Clone + Default + Debug>(&self, f: impl Fn(T) -> U) -> Point<U> {
Point {
x: f(self.x.clone()),
@ -120,6 +124,10 @@ where
},
}
}
pub fn clamp(&self, min: &Self, max: &Self) -> Self {
self.max(min).min(max)
}
}
impl<T: Clone + Default + Debug> Clone for Point<T> {
@ -259,6 +267,24 @@ impl From<Size<Pixels>> for Size<GlobalPixels> {
}
}
impl From<Size<Pixels>> for Size<DefiniteLength> {
fn from(size: Size<Pixels>) -> Self {
Size {
width: size.width.into(),
height: size.height.into(),
}
}
}
impl From<Size<Pixels>> for Size<AbsoluteLength> {
fn from(size: Size<Pixels>) -> Self {
Size {
width: size.width.into(),
height: size.height.into(),
}
}
}
impl Size<Length> {
pub fn full() -> Self {
Self {
@ -492,6 +518,15 @@ where
impl<T: Clone + Default + Debug + Copy> Copy for Edges<T> {}
impl<T: Clone + Default + Debug> Edges<T> {
pub fn all(value: T) -> Self {
Self {
top: value.clone(),
right: value.clone(),
bottom: value.clone(),
left: value,
}
}
pub fn map<U>(&self, f: impl Fn(&T) -> U) -> Edges<U>
where
U: Clone + Default + Debug,
@ -541,6 +576,15 @@ impl Edges<DefiniteLength> {
left: px(0.).into(),
}
}
pub fn to_pixels(&self, parent_size: Size<AbsoluteLength>, rem_size: Pixels) -> Edges<Pixels> {
Edges {
top: self.top.to_pixels(parent_size.height, rem_size),
right: self.right.to_pixels(parent_size.width, rem_size),
bottom: self.bottom.to_pixels(parent_size.height, rem_size),
left: self.left.to_pixels(parent_size.width, rem_size),
}
}
}
impl Edges<AbsoluteLength> {
@ -672,16 +716,16 @@ impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
pub struct Pixels(pub(crate) f32);
impl std::ops::Div for Pixels {
type Output = Self;
type Output = f32;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
self.0 / rhs.0
}
}
impl std::ops::DivAssign for Pixels {
fn div_assign(&mut self, rhs: Self) {
self.0 /= rhs.0;
*self = Self(self.0 / rhs.0);
}
}
@ -730,16 +774,9 @@ impl MulAssign<f32> for Pixels {
}
impl Pixels {
pub const ZERO: Pixels = Pixels(0.0);
pub const MAX: Pixels = Pixels(f32::MAX);
pub fn as_usize(&self) -> usize {
self.0 as usize
}
pub fn as_isize(&self) -> isize {
self.0 as isize
}
pub fn floor(&self) -> Self {
Self(self.0.floor())
}

View file

@ -1,3 +1,4 @@
#[macro_use]
mod action;
mod app;
mod assets;
@ -23,6 +24,7 @@ mod text_system;
mod util;
mod view;
mod window;
mod window_input_handler;
mod private {
/// A mechanism for restricting implementations of a trait to only those in GPUI.
@ -35,6 +37,7 @@ pub use anyhow::Result;
pub use app::*;
pub use assets::*;
pub use color::*;
pub use ctor::ctor;
pub use element::*;
pub use elements::*;
pub use executor::*;
@ -63,6 +66,7 @@ pub use text_system::*;
pub use util::arc_cow::ArcCow;
pub use view::*;
pub use window::*;
pub use window_input_handler::*;
use derive_more::{Deref, DerefMut};
use std::{

View file

@ -25,13 +25,13 @@ const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
pub trait StatelessInteractive<V: 'static>: Element<V> {
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V>;
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V>;
fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.stateless_interaction().hover_style = f(StyleRefinement::default());
self.stateless_interactivity().hover_style = f(StyleRefinement::default());
self
}
@ -43,7 +43,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().group_hover_style = Some(GroupStyle {
self.stateless_interactivity().group_hover_style = Some(GroupStyle {
group: group_name.into(),
style: f(StyleRefinement::default()),
});
@ -58,7 +58,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_down_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
@ -79,7 +79,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_up_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
@ -100,7 +100,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_down_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture
@ -121,7 +121,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_up_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture
@ -141,7 +141,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_move_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
@ -158,7 +158,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.scroll_wheel_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
@ -174,23 +174,48 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
C: TryInto<DispatchContext>,
C::Error: Debug,
{
self.stateless_interaction().dispatch_context =
self.stateless_interactivity().dispatch_context =
context.try_into().expect("invalid dispatch context");
self
}
fn on_action<A: 'static>(
/// Capture the given action, fires during the capture phase
fn capture_action<A: 'static>(
mut self,
listener: impl Fn(&mut V, &A, DispatchPhase, &mut ViewContext<V>) + 'static,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.stateless_interaction().key_listeners.push((
self.stateless_interactivity().key_listeners.push((
TypeId::of::<A>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
listener(view, event, phase, cx);
if phase == DispatchPhase::Capture {
listener(view, event, cx)
}
None
}),
));
self
}
/// Add a listener for the given action, fires during the bubble event phase
fn on_action<A: 'static>(
mut self,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.stateless_interactivity().key_listeners.push((
TypeId::of::<A>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
listener(view, event, cx)
}
None
}),
));
@ -204,7 +229,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().key_listeners.push((
self.stateless_interactivity().key_listeners.push((
TypeId::of::<KeyDownEvent>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
@ -222,7 +247,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().key_listeners.push((
self.stateless_interactivity().key_listeners.push((
TypeId::of::<KeyUpEvent>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
@ -237,7 +262,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.drag_over_styles
.push((TypeId::of::<S>(), f(StyleRefinement::default())));
self
@ -251,7 +276,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().group_drag_over_styles.push((
self.stateless_interactivity().group_drag_over_styles.push((
TypeId::of::<S>(),
GroupStyle {
group: group_name.into(),
@ -268,7 +293,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().drop_listeners.push((
self.stateless_interactivity().drop_listeners.push((
TypeId::of::<W>(),
Box::new(move |view, dragged_view, cx| {
listener(view, dragged_view.downcast().unwrap(), cx);
@ -279,13 +304,13 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
}
pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V>;
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V>;
fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.stateful_interaction().active_style = f(StyleRefinement::default());
self.stateful_interactivity().active_style = f(StyleRefinement::default());
self
}
@ -297,7 +322,7 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
where
Self: Sized,
{
self.stateful_interaction().group_active_style = Some(GroupStyle {
self.stateful_interactivity().group_active_style = Some(GroupStyle {
group: group_name.into(),
style: f(StyleRefinement::default()),
});
@ -311,7 +336,7 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
where
Self: Sized,
{
self.stateful_interaction()
self.stateful_interactivity()
.click_listeners
.push(Box::new(move |view, event, cx| listener(view, event, cx)));
self
@ -326,10 +351,10 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
W: 'static + Render,
{
debug_assert!(
self.stateful_interaction().drag_listener.is_none(),
self.stateful_interactivity().drag_listener.is_none(),
"calling on_drag more than once on the same element is not supported"
);
self.stateful_interaction().drag_listener =
self.stateful_interactivity().drag_listener =
Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
view: listener(view_state, cx).into(),
cursor_offset,
@ -342,10 +367,10 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
Self: Sized,
{
debug_assert!(
self.stateful_interaction().hover_listener.is_none(),
self.stateful_interactivity().hover_listener.is_none(),
"calling on_hover more than once on the same element is not supported"
);
self.stateful_interaction().hover_listener = Some(Box::new(listener));
self.stateful_interactivity().hover_listener = Some(Box::new(listener));
self
}
@ -358,10 +383,10 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
W: 'static + Render,
{
debug_assert!(
self.stateful_interaction().tooltip_builder.is_none(),
self.stateful_interactivity().tooltip_builder.is_none(),
"calling tooltip more than once on the same element is not supported"
);
self.stateful_interaction().tooltip_builder = Some(Arc::new(move |view_state, cx| {
self.stateful_interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
build_tooltip(view_state, cx).into()
}));
@ -369,11 +394,11 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
}
}
pub trait ElementInteraction<V: 'static>: 'static {
fn as_stateless(&self) -> &StatelessInteraction<V>;
fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V>;
fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
pub trait ElementInteractivity<V: 'static>: 'static {
fn as_stateless(&self) -> &StatelessInteractivity<V>;
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V>;
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>>;
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>>;
fn initialize<R>(
&mut self,
@ -397,9 +422,10 @@ pub trait ElementInteraction<V: 'static>: 'static {
None
}),
));
let result = stateful.stateless.initialize(cx, f);
stateful.key_listeners.pop();
result
cx.with_key_dispatch_context(stateful.dispatch_context.clone(), |cx| {
cx.with_key_listeners(mem::take(&mut stateful.key_listeners), f)
})
})
} else {
let stateless = self.as_stateless_mut();
@ -735,11 +761,11 @@ pub trait ElementInteraction<V: 'static>: 'static {
}
#[derive(Deref, DerefMut)]
pub struct StatefulInteraction<V> {
pub struct StatefulInteractivity<V> {
pub id: ElementId,
#[deref]
#[deref_mut]
stateless: StatelessInteraction<V>,
stateless: StatelessInteractivity<V>,
click_listeners: SmallVec<[ClickListener<V>; 2]>,
active_style: StyleRefinement,
group_active_style: Option<GroupStyle>,
@ -748,29 +774,29 @@ pub struct StatefulInteraction<V> {
tooltip_builder: Option<TooltipBuilder<V>>,
}
impl<V: 'static> ElementInteraction<V> for StatefulInteraction<V> {
fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
impl<V: 'static> ElementInteractivity<V> for StatefulInteractivity<V> {
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
Some(self)
}
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
Some(self)
}
fn as_stateless(&self) -> &StatelessInteraction<V> {
fn as_stateless(&self) -> &StatelessInteractivity<V> {
&self.stateless
}
fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
&mut self.stateless
}
}
impl<V> From<ElementId> for StatefulInteraction<V> {
impl<V> From<ElementId> for StatefulInteractivity<V> {
fn from(id: ElementId) -> Self {
Self {
id,
stateless: StatelessInteraction::default(),
stateless: StatelessInteractivity::default(),
click_listeners: SmallVec::new(),
drag_listener: None,
hover_listener: None,
@ -783,7 +809,7 @@ impl<V> From<ElementId> for StatefulInteraction<V> {
type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
pub struct StatelessInteraction<V> {
pub struct StatelessInteractivity<V> {
pub dispatch_context: DispatchContext,
pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
@ -797,9 +823,9 @@ pub struct StatelessInteraction<V> {
drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
}
impl<V> StatelessInteraction<V> {
pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
StatefulInteraction {
impl<V> StatelessInteractivity<V> {
pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteractivity<V> {
StatefulInteractivity {
id: id.into(),
stateless: self,
click_listeners: SmallVec::new(),
@ -875,9 +901,15 @@ impl InteractiveElementState {
.as_ref()
.map(|offset| offset.lock().clone())
}
pub fn track_scroll_offset(&mut self) -> Arc<Mutex<Point<Pixels>>> {
self.scroll_offset
.get_or_insert_with(|| Arc::new(Mutex::new(Default::default())))
.clone()
}
}
impl<V> Default for StatelessInteraction<V> {
impl<V> Default for StatelessInteractivity<V> {
fn default() -> Self {
Self {
dispatch_context: DispatchContext::default(),
@ -895,20 +927,20 @@ impl<V> Default for StatelessInteraction<V> {
}
}
impl<V: 'static> ElementInteraction<V> for StatelessInteraction<V> {
fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
impl<V: 'static> ElementInteractivity<V> for StatelessInteractivity<V> {
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
None
}
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
None
}
fn as_stateless(&self) -> &StatelessInteraction<V> {
fn as_stateless(&self) -> &StatelessInteractivity<V> {
self
}
fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
self
}
}
@ -1230,3 +1262,70 @@ pub type KeyListener<V> = Box<
) -> Option<Box<dyn Action>>
+ 'static,
>;
#[cfg(test)]
mod test {
use crate::{
self as gpui, div, Div, FocusHandle, KeyBinding, Keystroke, ParentElement, Render,
StatefulInteractivity, StatelessInteractive, TestAppContext, VisualContext,
};
struct TestView {
saw_key_down: bool,
saw_action: bool,
focus_handle: FocusHandle,
}
actions!(TestAction);
impl Render for TestView {
type Element = Div<Self, StatefulInteractivity<Self>>;
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
div().id("testview").child(
div()
.on_key_down(|this: &mut TestView, _, _, _| {
dbg!("ola!");
this.saw_key_down = true
})
.on_action(|this: &mut TestView, _: &TestAction, _| {
dbg!("ola!");
this.saw_action = true
})
.track_focus(&self.focus_handle),
)
}
}
#[gpui::test]
fn test_on_events(cx: &mut TestAppContext) {
let window = cx.update(|cx| {
cx.open_window(Default::default(), |cx| {
cx.build_view(|cx| TestView {
saw_key_down: false,
saw_action: false,
focus_handle: cx.focus_handle(),
})
})
});
cx.update(|cx| {
cx.bind_keys(vec![KeyBinding::new("ctrl-g", TestAction, None)]);
});
window
.update(cx, |test_view, cx| cx.focus(&test_view.focus_handle))
.unwrap();
cx.dispatch_keystroke(*window, Keystroke::parse("space").unwrap(), false);
cx.dispatch_keystroke(*window, Keystroke::parse("ctrl-g").unwrap(), false);
window
.update(cx, |test_view, _| {
assert!(test_view.saw_key_down || test_view.saw_action);
assert!(test_view.saw_key_down);
assert!(test_view.saw_action);
})
.unwrap();
}
}

View file

@ -87,7 +87,7 @@ impl MetalRenderer {
MTLResourceOptions::StorageModeManaged,
);
let paths_rasterization_pipeline_state = build_pipeline_state(
let paths_rasterization_pipeline_state = build_path_rasterization_pipeline_state(
&device,
&library,
"paths_rasterization",
@ -823,7 +823,40 @@ fn build_pipeline_state(
color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Invalid);
device
.new_render_pipeline_state(&descriptor)
.expect("could not create render pipeline state")
}
fn build_path_rasterization_pipeline_state(
device: &metal::DeviceRef,
library: &metal::LibraryRef,
label: &str,
vertex_fn_name: &str,
fragment_fn_name: &str,
pixel_format: metal::MTLPixelFormat,
) -> metal::RenderPipelineState {
let vertex_fn = library
.get_function(vertex_fn_name, None)
.expect("error locating vertex function");
let fragment_fn = library
.get_function(fragment_fn_name, None)
.expect("error locating fragment function");
let descriptor = metal::RenderPipelineDescriptor::new();
descriptor.set_label(label);
descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
color_attachment.set_pixel_format(pixel_format);
color_attachment.set_blending_enabled(true);
color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
device
.new_render_pipeline_state(&descriptor)

View file

@ -5,10 +5,11 @@ using namespace metal;
float4 hsla_to_rgba(Hsla hsla);
float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds,
Bounds_ScaledPixels clip_bounds,
constant Size_DevicePixels *viewport_size);
float2 to_tile_position(float2 unit_vertex, AtlasTile tile,
constant Size_DevicePixels *atlas_size);
float4 distance_from_clip_rect(float2 unit_vertex, Bounds_ScaledPixels bounds,
Bounds_ScaledPixels clip_bounds);
float quad_sdf(float2 point, Bounds_ScaledPixels bounds,
Corners_ScaledPixels corner_radii);
float gaussian(float x, float sigma);
@ -21,6 +22,14 @@ struct QuadVertexOutput {
float4 background_color [[flat]];
float4 border_color [[flat]];
uint quad_id [[flat]];
float clip_distance [[clip_distance]][4];
};
struct QuadFragmentInput {
float4 position [[position]];
float4 background_color [[flat]];
float4 border_color [[flat]];
uint quad_id [[flat]];
};
vertex QuadVertexOutput quad_vertex(uint unit_vertex_id [[vertex_id]],
@ -33,15 +42,21 @@ vertex QuadVertexOutput quad_vertex(uint unit_vertex_id [[vertex_id]],
[[buffer(QuadInputIndex_ViewportSize)]]) {
float2 unit_vertex = unit_vertices[unit_vertex_id];
Quad quad = quads[quad_id];
float4 device_position = to_device_position(
unit_vertex, quad.bounds, quad.content_mask.bounds, viewport_size);
float4 device_position =
to_device_position(unit_vertex, quad.bounds, viewport_size);
float4 clip_distance = distance_from_clip_rect(unit_vertex, quad.bounds,
quad.content_mask.bounds);
float4 background_color = hsla_to_rgba(quad.background);
float4 border_color = hsla_to_rgba(quad.border_color);
return QuadVertexOutput{device_position, background_color, border_color,
quad_id};
return QuadVertexOutput{
device_position,
background_color,
border_color,
quad_id,
{clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}};
}
fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
fragment float4 quad_fragment(QuadFragmentInput input [[stage_in]],
constant Quad *quads
[[buffer(QuadInputIndex_Quads)]]) {
Quad quad = quads[input.quad_id];
@ -117,6 +132,13 @@ struct ShadowVertexOutput {
float4 position [[position]];
float4 color [[flat]];
uint shadow_id [[flat]];
float clip_distance [[clip_distance]][4];
};
struct ShadowFragmentInput {
float4 position [[position]];
float4 color [[flat]];
uint shadow_id [[flat]];
};
vertex ShadowVertexOutput shadow_vertex(
@ -137,18 +159,20 @@ vertex ShadowVertexOutput shadow_vertex(
bounds.size.width += 2. * margin;
bounds.size.height += 2. * margin;
float4 device_position = to_device_position(
unit_vertex, bounds, shadow.content_mask.bounds, viewport_size);
float4 device_position =
to_device_position(unit_vertex, bounds, viewport_size);
float4 clip_distance =
distance_from_clip_rect(unit_vertex, bounds, shadow.content_mask.bounds);
float4 color = hsla_to_rgba(shadow.color);
return ShadowVertexOutput{
device_position,
color,
shadow_id,
};
{clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}};
}
fragment float4 shadow_fragment(ShadowVertexOutput input [[stage_in]],
fragment float4 shadow_fragment(ShadowFragmentInput input [[stage_in]],
constant Shadow *shadows
[[buffer(ShadowInputIndex_Shadows)]]) {
Shadow shadow = shadows[input.shadow_id];
@ -197,6 +221,13 @@ struct UnderlineVertexOutput {
float4 position [[position]];
float4 color [[flat]];
uint underline_id [[flat]];
float clip_distance [[clip_distance]][4];
};
struct UnderlineFragmentInput {
float4 position [[position]];
float4 color [[flat]];
uint underline_id [[flat]];
};
vertex UnderlineVertexOutput underline_vertex(
@ -208,13 +239,18 @@ vertex UnderlineVertexOutput underline_vertex(
float2 unit_vertex = unit_vertices[unit_vertex_id];
Underline underline = underlines[underline_id];
float4 device_position =
to_device_position(unit_vertex, underline.bounds,
underline.content_mask.bounds, viewport_size);
to_device_position(unit_vertex, underline.bounds, viewport_size);
float4 clip_distance = distance_from_clip_rect(unit_vertex, underline.bounds,
underline.content_mask.bounds);
float4 color = hsla_to_rgba(underline.color);
return UnderlineVertexOutput{device_position, color, underline_id};
return UnderlineVertexOutput{
device_position,
color,
underline_id,
{clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}};
}
fragment float4 underline_fragment(UnderlineVertexOutput input [[stage_in]],
fragment float4 underline_fragment(UnderlineFragmentInput input [[stage_in]],
constant Underline *underlines
[[buffer(UnderlineInputIndex_Underlines)]]) {
Underline underline = underlines[input.underline_id];
@ -244,7 +280,13 @@ struct MonochromeSpriteVertexOutput {
float4 position [[position]];
float2 tile_position;
float4 color [[flat]];
uint sprite_id [[flat]];
float clip_distance [[clip_distance]][4];
};
struct MonochromeSpriteFragmentInput {
float4 position [[position]];
float2 tile_position;
float4 color [[flat]];
};
vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
@ -255,32 +297,31 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
[[buffer(SpriteInputIndex_ViewportSize)]],
constant Size_DevicePixels *atlas_size
[[buffer(SpriteInputIndex_AtlasTextureSize)]]) {
float2 unit_vertex = unit_vertices[unit_vertex_id];
MonochromeSprite sprite = sprites[sprite_id];
// Don't apply content mask at the vertex level because we don't have time
// to make sampling from the texture match the mask.
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
sprite.bounds, viewport_size);
float4 device_position =
to_device_position(unit_vertex, sprite.bounds, viewport_size);
float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds,
sprite.content_mask.bounds);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
float4 color = hsla_to_rgba(sprite.color);
return MonochromeSpriteVertexOutput{device_position, tile_position, color,
sprite_id};
return MonochromeSpriteVertexOutput{
device_position,
tile_position,
color,
{clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}};
}
fragment float4 monochrome_sprite_fragment(
MonochromeSpriteVertexOutput input [[stage_in]],
MonochromeSpriteFragmentInput input [[stage_in]],
constant MonochromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
texture2d<float> atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]]) {
MonochromeSprite sprite = sprites[input.sprite_id];
constexpr sampler atlas_texture_sampler(mag_filter::linear,
min_filter::linear);
float4 sample =
atlas_texture.sample(atlas_texture_sampler, input.tile_position);
float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
Corners_ScaledPixels{0., 0., 0., 0.});
float4 color = input.color;
color.a *= sample.a * saturate(0.5 - clip_distance);
color.a *= sample.a;
return color;
}
@ -288,6 +329,13 @@ struct PolychromeSpriteVertexOutput {
float4 position [[position]];
float2 tile_position;
uint sprite_id [[flat]];
float clip_distance [[clip_distance]][4];
};
struct PolychromeSpriteFragmentInput {
float4 position [[position]];
float2 tile_position;
uint sprite_id [[flat]];
};
vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
@ -301,17 +349,20 @@ vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
float2 unit_vertex = unit_vertices[unit_vertex_id];
PolychromeSprite sprite = sprites[sprite_id];
// Don't apply content mask at the vertex level because we don't have time
// to make sampling from the texture match the mask.
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
sprite.bounds, viewport_size);
float4 device_position =
to_device_position(unit_vertex, sprite.bounds, viewport_size);
float4 clip_distance = distance_from_clip_rect(unit_vertex, sprite.bounds,
sprite.content_mask.bounds);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
return PolychromeSpriteVertexOutput{device_position, tile_position,
sprite_id};
return PolychromeSpriteVertexOutput{
device_position,
tile_position,
sprite_id,
{clip_distance.x, clip_distance.y, clip_distance.z, clip_distance.w}};
}
fragment float4 polychrome_sprite_fragment(
PolychromeSpriteVertexOutput input [[stage_in]],
PolychromeSpriteFragmentInput input [[stage_in]],
constant PolychromeSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
texture2d<float> atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]]) {
PolychromeSprite sprite = sprites[input.sprite_id];
@ -319,11 +370,8 @@ fragment float4 polychrome_sprite_fragment(
min_filter::linear);
float4 sample =
atlas_texture.sample(atlas_texture_sampler, input.tile_position);
float quad_distance =
float distance =
quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii);
float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
Corners_ScaledPixels{0., 0., 0., 0.});
float distance = max(quad_distance, clip_distance);
float4 color = sample;
if (sprite.grayscale) {
@ -385,7 +433,6 @@ struct PathSpriteVertexOutput {
float4 position [[position]];
float2 tile_position;
float4 color [[flat]];
uint sprite_id [[flat]];
};
vertex PathSpriteVertexOutput path_sprite_vertex(
@ -401,19 +448,17 @@ vertex PathSpriteVertexOutput path_sprite_vertex(
PathSprite sprite = sprites[sprite_id];
// Don't apply content mask because it was already accounted for when
// rasterizing the path.
float4 device_position = to_device_position(unit_vertex, sprite.bounds,
sprite.bounds, viewport_size);
float4 device_position =
to_device_position(unit_vertex, sprite.bounds, viewport_size);
float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
float4 color = hsla_to_rgba(sprite.color);
return PathSpriteVertexOutput{device_position, tile_position, color,
sprite_id};
return PathSpriteVertexOutput{device_position, tile_position, color};
}
fragment float4 path_sprite_fragment(
PathSpriteVertexOutput input [[stage_in]],
constant PathSprite *sprites [[buffer(SpriteInputIndex_Sprites)]],
texture2d<float> atlas_texture [[texture(SpriteInputIndex_AtlasTexture)]]) {
PathSprite sprite = sprites[input.sprite_id];
constexpr sampler atlas_texture_sampler(mag_filter::linear,
min_filter::linear);
float4 sample =
@ -473,16 +518,10 @@ float4 hsla_to_rgba(Hsla hsla) {
}
float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds,
Bounds_ScaledPixels clip_bounds,
constant Size_DevicePixels *input_viewport_size) {
float2 position =
unit_vertex * float2(bounds.size.width, bounds.size.height) +
float2(bounds.origin.x, bounds.origin.y);
position.x = max(clip_bounds.origin.x, position.x);
position.x = min(clip_bounds.origin.x + clip_bounds.size.width, position.x);
position.y = max(clip_bounds.origin.y, position.y);
position.y = min(clip_bounds.origin.y + clip_bounds.size.height, position.y);
float2 viewport_size = float2((float)input_viewport_size->width,
(float)input_viewport_size->height);
float2 device_position =
@ -551,3 +590,14 @@ float blur_along_x(float x, float y, float sigma, float corner,
0.5 + 0.5 * erf((x + float2(-curved, curved)) * (sqrt(0.5) / sigma));
return integral.y - integral.x;
}
float4 distance_from_clip_rect(float2 unit_vertex, Bounds_ScaledPixels bounds,
Bounds_ScaledPixels clip_bounds) {
float2 position =
unit_vertex * float2(bounds.size.width, bounds.size.height) +
float2(bounds.origin.x, bounds.origin.y);
return float4(position.x - clip_bounds.origin.x,
clip_bounds.origin.x + clip_bounds.size.width - position.x,
position.y - clip_bounds.origin.y,
clip_bounds.origin.y + clip_bounds.size.height - position.y);
}

View file

@ -1,5 +1,9 @@
mod dispatcher;
mod display;
mod platform;
mod window;
pub use dispatcher::*;
pub use display::*;
pub use platform::*;
pub use window::*;

View file

@ -0,0 +1,41 @@
use anyhow::{Ok, Result};
use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point};
#[derive(Debug)]
pub struct TestDisplay {
id: DisplayId,
uuid: uuid::Uuid,
bounds: Bounds<GlobalPixels>,
}
impl TestDisplay {
pub fn new() -> Self {
TestDisplay {
id: DisplayId(1),
uuid: uuid::Uuid::new_v4(),
bounds: Bounds::from_corners(
Point::zero(),
Point::new(GlobalPixels(1920.), GlobalPixels(1080.)),
),
}
}
}
impl PlatformDisplay for TestDisplay {
fn id(&self) -> crate::DisplayId {
self.id
}
fn uuid(&self) -> Result<uuid::Uuid> {
Ok(self.uuid)
}
fn as_any(&self) -> &dyn std::any::Any {
todo!()
}
fn bounds(&self) -> crate::Bounds<crate::GlobalPixels> {
self.bounds
}
}

View file

@ -1,10 +1,18 @@
use crate::{BackgroundExecutor, DisplayId, ForegroundExecutor, Platform, PlatformTextSystem};
use crate::{
AnyWindowHandle, BackgroundExecutor, CursorStyle, DisplayId, ForegroundExecutor, Platform,
PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
};
use anyhow::{anyhow, Result};
use std::sync::Arc;
use parking_lot::Mutex;
use std::{rc::Rc, sync::Arc};
pub struct TestPlatform {
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
active_display: Rc<dyn PlatformDisplay>,
active_cursor: Mutex<CursorStyle>,
}
impl TestPlatform {
@ -12,6 +20,10 @@ impl TestPlatform {
TestPlatform {
background_executor: executor,
foreground_executor,
active_cursor: Default::default(),
active_display: Rc::new(TestDisplay::new()),
active_window: Default::default(),
}
}
}
@ -59,11 +71,11 @@ impl Platform for TestPlatform {
}
fn displays(&self) -> Vec<std::rc::Rc<dyn crate::PlatformDisplay>> {
unimplemented!()
vec![self.active_display.clone()]
}
fn display(&self, _id: DisplayId) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
unimplemented!()
fn display(&self, id: DisplayId) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
self.displays().iter().find(|d| d.id() == id).cloned()
}
fn main_window(&self) -> Option<crate::AnyWindowHandle> {
@ -72,10 +84,11 @@ impl Platform for TestPlatform {
fn open_window(
&self,
_handle: crate::AnyWindowHandle,
_options: crate::WindowOptions,
handle: AnyWindowHandle,
options: WindowOptions,
) -> Box<dyn crate::PlatformWindow> {
unimplemented!()
*self.active_window.lock() = Some(handle);
Box::new(TestWindow::new(options, self.active_display.clone()))
}
fn set_display_link_output_callback(
@ -164,8 +177,8 @@ impl Platform for TestPlatform {
unimplemented!()
}
fn set_cursor_style(&self, _style: crate::CursorStyle) {
unimplemented!()
fn set_cursor_style(&self, style: crate::CursorStyle) {
*self.active_cursor.lock() = style;
}
fn should_auto_hide_scrollbars(&self) -> bool {

View file

@ -0,0 +1,179 @@
use std::{rc::Rc, sync::Arc};
use parking_lot::Mutex;
use crate::{
px, Pixels, PlatformAtlas, PlatformDisplay, PlatformWindow, Point, Scene, Size,
WindowAppearance, WindowBounds, WindowOptions,
};
#[derive(Default)]
struct Handlers {
active_status_change: Vec<Box<dyn FnMut(bool)>>,
input: Vec<Box<dyn FnMut(crate::InputEvent) -> bool>>,
moved: Vec<Box<dyn FnMut()>>,
resize: Vec<Box<dyn FnMut(Size<Pixels>, f32)>>,
}
pub struct TestWindow {
bounds: WindowBounds,
current_scene: Mutex<Option<Scene>>,
display: Rc<dyn PlatformDisplay>,
handlers: Mutex<Handlers>,
sprite_atlas: Arc<dyn PlatformAtlas>,
}
impl TestWindow {
pub fn new(options: WindowOptions, display: Rc<dyn PlatformDisplay>) -> Self {
Self {
bounds: options.bounds,
current_scene: Default::default(),
display,
sprite_atlas: Arc::new(TestAtlas),
handlers: Default::default(),
}
}
}
impl PlatformWindow for TestWindow {
fn bounds(&self) -> WindowBounds {
self.bounds
}
fn content_size(&self) -> Size<Pixels> {
let bounds = match self.bounds {
WindowBounds::Fixed(bounds) => bounds,
WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().bounds(),
};
bounds.size.map(|p| px(p.0))
}
fn scale_factor(&self) -> f32 {
2.0
}
fn titlebar_height(&self) -> Pixels {
todo!()
}
fn appearance(&self) -> WindowAppearance {
todo!()
}
fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
self.display.clone()
}
fn mouse_position(&self) -> Point<Pixels> {
Point::zero()
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
todo!()
}
fn set_input_handler(&mut self, _input_handler: Box<dyn crate::PlatformInputHandler>) {
todo!()
}
fn prompt(
&self,
_level: crate::PromptLevel,
_msg: &str,
_answers: &[&str],
) -> futures::channel::oneshot::Receiver<usize> {
todo!()
}
fn activate(&self) {
todo!()
}
fn set_title(&mut self, _title: &str) {
todo!()
}
fn set_edited(&mut self, _edited: bool) {
todo!()
}
fn show_character_palette(&self) {
todo!()
}
fn minimize(&self) {
todo!()
}
fn zoom(&self) {
todo!()
}
fn toggle_full_screen(&self) {
todo!()
}
fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
self.handlers.lock().input.push(callback)
}
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
self.handlers.lock().active_status_change.push(callback)
}
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
self.handlers.lock().resize.push(callback)
}
fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {
todo!()
}
fn on_moved(&self, callback: Box<dyn FnMut()>) {
self.handlers.lock().moved.push(callback)
}
fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {
todo!()
}
fn on_close(&self, _callback: Box<dyn FnOnce()>) {
todo!()
}
fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {
todo!()
}
fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
todo!()
}
fn draw(&self, scene: crate::Scene) {
self.current_scene.lock().replace(scene);
}
fn sprite_atlas(&self) -> std::sync::Arc<dyn crate::PlatformAtlas> {
self.sprite_atlas.clone()
}
}
pub struct TestAtlas;
impl PlatformAtlas for TestAtlas {
fn get_or_insert_with<'a>(
&self,
_key: &crate::AtlasKey,
_build: &mut dyn FnMut() -> anyhow::Result<(
Size<crate::DevicePixels>,
std::borrow::Cow<'a, [u8]>,
)>,
) -> anyhow::Result<crate::AtlasTile> {
todo!()
}
fn clear(&self) {
todo!()
}
}

View file

@ -1,8 +1,8 @@
use crate::{
black, phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
Corners, CornersRefinement, CursorStyle, DefiniteLength, Edges, EdgesRefinement, Font,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems,
Result, Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext, WindowContext,
FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Result,
Rgba, SharedString, Size, SizeRefinement, Styled, TextRun, ViewContext, WindowContext,
};
use refineable::{Cascade, Refineable};
use smallvec::SmallVec;
@ -134,7 +134,7 @@ pub struct TextStyle {
pub color: Hsla,
pub font_family: SharedString,
pub font_features: FontFeatures,
pub font_size: Rems,
pub font_size: AbsoluteLength,
pub line_height: DefiniteLength,
pub font_weight: FontWeight,
pub font_style: FontStyle,
@ -147,7 +147,7 @@ impl Default for TextStyle {
color: black(),
font_family: "Helvetica".into(), // todo!("Get a font we know exists on the system")
font_features: FontFeatures::default(),
font_size: rems(1.),
font_size: rems(1.).into(),
line_height: phi(),
font_weight: FontWeight::default(),
font_style: FontStyle::default(),
@ -189,6 +189,10 @@ impl TextStyle {
}
}
pub fn line_height_in_pixels(&self, rem_size: Pixels) -> Pixels {
self.line_height.to_pixels(self.font_size, rem_size)
}
pub fn to_run(&self, len: usize) -> TextRun {
TextRun {
len,
@ -277,7 +281,7 @@ impl Style {
pub fn paint<V: 'static>(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
let rem_size = cx.rem_size();
cx.stack(0, |cx| {
cx.with_z_index(0, |cx| {
cx.paint_shadows(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),
@ -287,7 +291,7 @@ impl Style {
let background_color = self.background.as_ref().and_then(Fill::color);
if background_color.is_some() || self.is_border_visible() {
cx.stack(1, |cx| {
cx.with_z_index(1, |cx| {
cx.paint_quad(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),

View file

@ -1,14 +1,19 @@
use crate::{
self as gpui, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength,
Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString,
StyleRefinement, Visibility,
self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position,
SharedString, Style, StyleRefinement, Visibility,
};
use crate::{BoxShadow, TextStyleRefinement};
use refineable::Refineable;
use smallvec::smallvec;
pub trait Styled {
fn style(&mut self) -> &mut StyleRefinement;
fn computed_style(&mut self) -> Style {
Style::default().refined(self.style().clone())
}
gpui2_macros::style_helpers!();
/// Sets the size of the element to the full width and height.
@ -433,7 +438,7 @@ pub trait Styled {
self
}
fn text_size(mut self, size: impl Into<Rems>) -> Self
fn text_size(mut self, size: impl Into<AbsoluteLength>) -> Self
where
Self: Sized,
{
@ -449,7 +454,7 @@ pub trait Styled {
{
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.75));
.font_size = Some(rems(0.75).into());
self
}
@ -459,7 +464,7 @@ pub trait Styled {
{
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(0.875));
.font_size = Some(rems(0.875).into());
self
}
@ -469,7 +474,7 @@ pub trait Styled {
{
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.0));
.font_size = Some(rems(1.0).into());
self
}
@ -479,7 +484,7 @@ pub trait Styled {
{
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.125));
.font_size = Some(rems(1.125).into());
self
}
@ -489,7 +494,7 @@ pub trait Styled {
{
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.25));
.font_size = Some(rems(1.25).into());
self
}
@ -499,7 +504,7 @@ pub trait Styled {
{
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.5));
.font_size = Some(rems(1.5).into());
self
}
@ -509,7 +514,7 @@ pub trait Styled {
{
self.text_style()
.get_or_insert_with(Default::default)
.font_size = Some(rems(1.875));
.font_size = Some(rems(1.875).into());
self
}

View file

@ -29,10 +29,6 @@ impl Line {
)
}
pub fn width(&self) -> Pixels {
self.layout.width
}
pub fn wrap_count(&self) -> usize {
self.layout.wrap_boundaries.len()
}
@ -78,7 +74,6 @@ impl Line {
glyph_origin.y += line_height;
}
prev_glyph_position = glyph.position;
let glyph_origin = glyph_origin + baseline_offset;
let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
if glyph.index >= run_end {
@ -129,14 +124,14 @@ impl Line {
if max_glyph_bounds.intersects(&content_mask.bounds) {
if glyph.is_emoji {
cx.paint_emoji(
glyph_origin,
glyph_origin + baseline_offset,
run.font_id,
glyph.id,
self.layout.layout.font_size,
)?;
} else {
cx.paint_glyph(
glyph_origin,
glyph_origin + baseline_offset,
run.font_id,
glyph.id,
self.layout.layout.font_size,

View file

@ -82,18 +82,6 @@ impl LineLayout {
self.width
}
pub fn font_for_index(&self, index: usize) -> Option<FontId> {
for run in &self.runs {
for glyph in &run.glyphs {
if glyph.index >= index {
return Some(run.font_id);
}
}
}
None
}
fn compute_wrap_boundaries(
&self,
text: &str,

View file

@ -2,13 +2,14 @@ use crate::{
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
Bounds, BoxShadow, Context, Corners, CursorStyle, DevicePixels, DispatchContext, DisplayId,
Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId,
GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay,
PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams,
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
VisualContext, WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
GlobalElementId, GlyphId, Hsla, ImageData, InputEvent, InputHandler, IsZero, KeyListener,
KeyMatch, KeyMatcher, Keystroke, LayoutId, Model, ModelContext, Modifiers, MonochromeSprite,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel,
Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription,
TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
WindowBounds, WindowInputHandler, WindowOptions, SUBPIXEL_VARIANTS,
};
use anyhow::{anyhow, Result};
use collections::HashMap;
@ -191,6 +192,7 @@ pub struct Window {
default_prevented: bool,
mouse_position: Point<Pixels>,
requested_cursor_style: Option<CursorStyle>,
requested_input_handler: Option<Box<dyn PlatformInputHandler>>,
scale_factor: f32,
bounds: WindowBounds,
bounds_observers: SubscriberSet<(), AnyObserver>,
@ -253,7 +255,7 @@ impl Window {
handle
.update(&mut cx, |_, cx| cx.dispatch_event(event))
.log_err()
.unwrap_or(true)
.unwrap_or(false)
})
});
@ -285,6 +287,7 @@ impl Window {
default_prevented: true,
mouse_position,
requested_cursor_style: None,
requested_input_handler: None,
scale_factor,
bounds,
bounds_observers: SubscriberSet::new(),
@ -300,7 +303,8 @@ impl Window {
/// When constructing the element tree, we maintain a stack of key dispatch frames until we
/// find the focused element. We interleave key listeners with dispatch contexts so we can use the
/// contexts when matching key events against the keymap.
/// contexts when matching key events against the keymap. A key listener can be either an action
/// handler or a [KeyDown] / [KeyUp] event listener.
enum KeyDispatchStackFrame {
Listener {
event_type: TypeId,
@ -559,6 +563,12 @@ impl<'a> WindowContext<'a> {
.request_measured_layout(style, rem_size, measure)
}
pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
self.window
.layout_engine
.compute_layout(layout_id, available_space)
}
/// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
/// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
/// in order to pass your element its `Bounds` automatically.
@ -604,6 +614,10 @@ impl<'a> WindowContext<'a> {
.find(|display| display.id() == self.window.display_id)
}
pub fn show_character_palette(&self) {
self.window.platform_window.show_character_palette();
}
/// The scale factor of the display associated with the window. For example, it could
/// return 2.0 for a "retina" display, indicating that each logical pixel should actually
/// be rendered as two pixels on screen.
@ -786,6 +800,7 @@ impl<'a> WindowContext<'a> {
}
/// Paint a monochrome (non-emoji) glyph into the scene for the current frame at the current z-index.
/// The y component of the origin is the baseline of the glyph.
pub fn paint_glyph(
&mut self,
origin: Point<Pixels>,
@ -839,6 +854,7 @@ impl<'a> WindowContext<'a> {
}
/// Paint an emoji glyph into the scene for the current frame at the current z-index.
/// The y component of the origin is the baseline of the glyph.
pub fn paint_emoji(
&mut self,
origin: Point<Pixels>,
@ -1007,6 +1023,9 @@ impl<'a> WindowContext<'a> {
.take()
.unwrap_or(CursorStyle::Arrow);
self.platform.set_cursor_style(cursor_style);
if let Some(handler) = self.window.requested_input_handler.take() {
self.window.platform_window.set_input_handler(handler);
}
self.window.dirty = false;
}
@ -1047,7 +1066,11 @@ impl<'a> WindowContext<'a> {
}
/// Dispatch a mouse or keyboard event on the window.
fn dispatch_event(&mut self, event: InputEvent) -> bool {
pub fn dispatch_event(&mut self, event: InputEvent) -> bool {
// Handlers may set this to false by calling `stop_propagation`
self.app.propagate_event = true;
self.window.default_prevented = false;
let event = match event {
// Track the mouse position with our own state, since accessing the platform
// API for the mouse position can only occur on the main thread.
@ -1101,10 +1124,6 @@ impl<'a> WindowContext<'a> {
};
if let Some(any_mouse_event) = event.mouse_event() {
// Handlers may set this to false by calling `stop_propagation`
self.app.propagate_event = true;
self.window.default_prevented = false;
if let Some(mut handlers) = self
.window
.mouse_listeners
@ -1151,6 +1170,7 @@ impl<'a> WindowContext<'a> {
.insert(any_mouse_event.type_id(), handlers);
}
} else if let Some(any_key_event) = event.keyboard_event() {
let mut did_handle_action = false;
let key_dispatch_stack = mem::take(&mut self.window.key_dispatch_stack);
let key_event_type = any_key_event.type_id();
let mut context_stack = SmallVec::<[&DispatchContext; 16]>::new();
@ -1171,6 +1191,7 @@ impl<'a> WindowContext<'a> {
self.dispatch_action(action, &key_dispatch_stack[..ix]);
}
if !self.app.propagate_event {
did_handle_action = true;
break;
}
}
@ -1199,6 +1220,7 @@ impl<'a> WindowContext<'a> {
}
if !self.app.propagate_event {
did_handle_action = true;
break;
}
}
@ -1212,6 +1234,7 @@ impl<'a> WindowContext<'a> {
drop(context_stack);
self.window.key_dispatch_stack = key_dispatch_stack;
return did_handle_action;
}
true
@ -1314,6 +1337,7 @@ impl<'a> WindowContext<'a> {
} = stack_frame
{
if action_type == *event_type {
self.app.propagate_event = false;
listener(action.as_any(), &[], DispatchPhase::Bubble, self);
if !self.app.propagate_event {
break;
@ -1328,6 +1352,7 @@ impl<'a> WindowContext<'a> {
self.app.global_action_listeners.remove(&action_type)
{
for listener in global_listeners.iter().rev() {
self.app.propagate_event = false;
listener(action.as_ref(), DispatchPhase::Bubble, self);
if !self.app.propagate_event {
break;
@ -1702,8 +1727,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
&mut self.window_cx
}
pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.z_index_stack.push(order);
pub fn with_z_index<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.z_index_stack.push(z_index);
let result = f(self);
self.window.z_index_stack.pop();
result
@ -2004,6 +2029,19 @@ impl<'a, V: 'static> ViewContext<'a, V> {
}
}
impl<V> ViewContext<'_, V>
where
V: InputHandler + 'static,
{
pub fn handle_text_input(&mut self) {
self.window.requested_input_handler = Some(Box::new(WindowInputHandler {
cx: self.app.this.clone(),
window: self.window_handle(),
handler: self.view().downgrade(),
}));
}
}
impl<V> ViewContext<'_, V>
where
V: EventEmitter,
@ -2058,9 +2096,9 @@ impl<V> Context for ViewContext<'_, V> {
impl<V: 'static> VisualContext for ViewContext<'_, V> {
fn build_view<W: 'static>(
&mut self,
build_view: impl FnOnce(&mut ViewContext<'_, W>) -> W,
build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W,
) -> Self::Result<View<W>> {
self.window_cx.build_view(build_view)
self.window_cx.build_view(build_view_state)
}
fn update_view<V2: 'static, R>(

View file

@ -0,0 +1,89 @@
use crate::{AnyWindowHandle, AppCell, Context, PlatformInputHandler, ViewContext, WeakView};
use std::{ops::Range, rc::Weak};
pub struct WindowInputHandler<V>
where
V: InputHandler,
{
pub cx: Weak<AppCell>,
pub window: AnyWindowHandle,
pub handler: WeakView<V>,
}
impl<V: InputHandler + 'static> PlatformInputHandler for WindowInputHandler<V> {
fn selected_text_range(&self) -> Option<std::ops::Range<usize>> {
self.update(|view, cx| view.selected_text_range(cx))
.flatten()
}
fn marked_text_range(&self) -> Option<std::ops::Range<usize>> {
self.update(|view, cx| view.marked_text_range(cx)).flatten()
}
fn text_for_range(&self, range_utf16: std::ops::Range<usize>) -> Option<String> {
self.update(|view, cx| view.text_for_range(range_utf16, cx))
.flatten()
}
fn replace_text_in_range(
&mut self,
replacement_range: Option<std::ops::Range<usize>>,
text: &str,
) {
self.update(|view, cx| view.replace_text_in_range(replacement_range, text, cx));
}
fn replace_and_mark_text_in_range(
&mut self,
range_utf16: Option<std::ops::Range<usize>>,
new_text: &str,
new_selected_range: Option<std::ops::Range<usize>>,
) {
self.update(|view, cx| {
view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
});
}
fn unmark_text(&mut self) {
self.update(|view, cx| view.unmark_text(cx));
}
fn bounds_for_range(&self, range_utf16: std::ops::Range<usize>) -> Option<crate::Bounds<f32>> {
self.update(|view, cx| view.bounds_for_range(range_utf16, cx))
.flatten()
}
}
impl<V: InputHandler + 'static> WindowInputHandler<V> {
fn update<T>(&self, f: impl FnOnce(&mut V, &mut ViewContext<V>) -> T) -> Option<T> {
let cx = self.cx.upgrade()?;
let mut cx = cx.borrow_mut();
cx.update_window(self.window, |_, cx| self.handler.update(cx, f).ok())
.ok()?
}
}
pub trait InputHandler: Sized {
fn text_for_range(&self, range: Range<usize>, cx: &mut ViewContext<Self>) -> Option<String>;
fn selected_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
fn replace_text_in_range(
&mut self,
range: Option<Range<usize>>,
text: &str,
cx: &mut ViewContext<Self>,
);
fn replace_and_mark_text_in_range(
&mut self,
range: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
cx: &mut ViewContext<Self>,
);
fn bounds_for_range(
&self,
range_utf16: std::ops::Range<usize>,
cx: &mut ViewContext<Self>,
) -> Option<crate::Bounds<f32>>;
}

View file

@ -0,0 +1,55 @@
// Input:
//
// #[action]
// struct Foo {
// bar: String,
// }
// Output:
//
// #[gpui::register_action]
// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
// struct Foo {
// bar: String,
// }
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
pub fn action(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let name = &input.ident;
let attrs = input
.attrs
.into_iter()
.filter(|attr| !attr.path.is_ident("action"))
.collect::<Vec<_>>();
let attributes = quote! {
#[gpui::register_action]
#[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
#(#attrs)*
};
let visibility = input.vis;
let output = match input.data {
syn::Data::Struct(ref struct_data) => {
let fields = &struct_data.fields;
quote! {
#attributes
#visibility struct #name #fields
}
}
syn::Data::Enum(ref enum_data) => {
let variants = &enum_data.variants;
quote! {
#attributes
#visibility enum #name { #variants }
}
}
_ => panic!("Expected a struct or an enum."),
};
TokenStream::from(output)
}

View file

@ -1,14 +1,26 @@
use proc_macro::TokenStream;
mod action;
mod derive_component;
mod register_action;
mod style_helpers;
mod test;
use proc_macro::TokenStream;
#[proc_macro]
pub fn style_helpers(args: TokenStream) -> TokenStream {
style_helpers::style_helpers(args)
}
#[proc_macro_attribute]
pub fn action(attr: TokenStream, item: TokenStream) -> TokenStream {
action::action(attr, item)
}
#[proc_macro_attribute]
pub fn register_action(attr: TokenStream, item: TokenStream) -> TokenStream {
register_action::register_action(attr, item)
}
#[proc_macro_derive(Component, attributes(component))]
pub fn derive_component(input: TokenStream) -> TokenStream {
derive_component::derive_component(input)

View file

@ -0,0 +1,33 @@
// Input:
//
// struct FooBar {}
// Output:
//
// struct FooBar {}
//
// #[allow(non_snake_case)]
// #[gpui2::ctor]
// fn register_foobar_builder() {
// gpui2::register_action_builder::<Foo>()
// }
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};
pub fn register_action(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as DeriveInput);
let type_name = &input.ident;
let ctor_fn_name = format_ident!("register_{}_builder", type_name.to_string().to_lowercase());
let expanded = quote! {
#input
#[allow(non_snake_case)]
#[gpui::ctor]
fn #ctor_fn_name() {
gpui::register_action::<#type_name>()
}
};
TokenStream::from(expanded)
}

View file

@ -95,6 +95,8 @@ mod tests {
.iter()
.map(|(name, color)| (name.to_string(), (*color).into()))
.collect(),
inlay_style: HighlightStyle::default(),
suggestion_style: HighlightStyle::default(),
};
let capture_names = &[

View file

@ -43,7 +43,7 @@ use std::{
},
};
use syntax_map::SyntaxSnapshot;
use theme::{SyntaxTheme, ThemeVariant};
use theme::{SyntaxTheme, Theme};
use tree_sitter::{self, Query};
use unicase::UniCase;
use util::{http::HttpClient, paths::PathExt};
@ -643,7 +643,7 @@ struct LanguageRegistryState {
next_available_language_id: AvailableLanguageId,
loading_languages: HashMap<AvailableLanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>,
subscription: (watch::Sender<()>, watch::Receiver<()>),
theme: Option<Arc<ThemeVariant>>,
theme: Option<Arc<Theme>>,
version: usize,
reload_count: usize,
}
@ -744,7 +744,7 @@ impl LanguageRegistry {
self.state.read().reload_count
}
pub fn set_theme(&self, theme: Arc<ThemeVariant>) {
pub fn set_theme(&self, theme: Arc<Theme>) {
let mut state = self.state.write();
state.theme = Some(theme.clone());
for language in &state.languages {

View file

@ -9,4 +9,5 @@ path = "src/menu2.rs"
doctest = false
[dependencies]
gpui = { package = "gpui2", path = "../gpui2" }
serde.workspace = true
serde_derive.workspace = true

View file

@ -1,25 +1,25 @@
// todo!(use actions! macro)
use serde_derive::Deserialize;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct Cancel;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct Confirm;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SecondaryConfirm;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectPrev;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectNext;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectFirst;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectLast;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct ShowContextMenu;

28
crates/picker2/Cargo.toml Normal file
View file

@ -0,0 +1,28 @@
[package]
name = "picker2"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/picker2.rs"
doctest = false
[dependencies]
editor = { package = "editor2", path = "../editor2" }
gpui = { package = "gpui2", path = "../gpui2" }
menu = { package = "menu2", path = "../menu2" }
settings = { package = "settings2", path = "../settings2" }
util = { path = "../util" }
theme = { package = "theme2", path = "../theme2" }
workspace = { package = "workspace2", path = "../workspace2" }
parking_lot.workspace = true
[dev-dependencies]
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
serde_json.workspace = true
workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
ctor.workspace = true
env_logger.workspace = true

View file

@ -0,0 +1,163 @@
use editor::Editor;
use gpui::{
div, uniform_list, Component, Div, FocusEnabled, ParentElement, Render, StatefulInteractivity,
StatelessInteractive, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext,
WindowContext,
};
use std::cmp;
pub struct Picker<D: PickerDelegate> {
pub delegate: D,
scroll_handle: UniformListScrollHandle,
editor: View<Editor>,
pending_update_matches: Option<Task<Option<()>>>,
}
pub trait PickerDelegate: Sized + 'static {
type ListItem: Component<Picker<Self>>;
fn match_count(&self) -> usize;
fn selected_index(&self) -> usize;
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
// fn placeholder_text(&self) -> Arc<str>;
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
fn render_match(
&self,
ix: usize,
selected: bool,
cx: &mut ViewContext<Picker<Self>>,
) -> Self::ListItem;
}
impl<D: PickerDelegate> Picker<D> {
pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
let editor = cx.build_view(|cx| Editor::single_line(cx));
cx.subscribe(&editor, Self::on_input_editor_event).detach();
Self {
delegate,
scroll_handle: UniformListScrollHandle::new(),
pending_update_matches: None,
editor,
}
}
pub fn focus(&self, cx: &mut WindowContext) {
self.editor.update(cx, |editor, cx| editor.focus(cx));
}
fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count();
if count > 0 {
let index = self.delegate.selected_index();
let ix = cmp::min(index + 1, count - 1);
self.delegate.set_selected_index(ix, cx);
self.scroll_handle.scroll_to_item(ix);
}
}
fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count();
if count > 0 {
let index = self.delegate.selected_index();
let ix = index.saturating_sub(1);
self.delegate.set_selected_index(ix, cx);
self.scroll_handle.scroll_to_item(ix);
}
}
fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count();
if count > 0 {
self.delegate.set_selected_index(0, cx);
self.scroll_handle.scroll_to_item(0);
}
}
fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
let count = self.delegate.match_count();
if count > 0 {
self.delegate.set_selected_index(count - 1, cx);
self.scroll_handle.scroll_to_item(count - 1);
}
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
self.delegate.dismissed(cx);
}
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
self.delegate.confirm(false, cx);
}
fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
self.delegate.confirm(true, cx);
}
fn on_input_editor_event(
&mut self,
_: View<Editor>,
event: &editor::Event,
cx: &mut ViewContext<Self>,
) {
if let editor::Event::BufferEdited = event {
let query = self.editor.read(cx).text(cx);
self.update_matches(query, cx);
}
}
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
let update = self.delegate.update_matches(query, cx);
self.matches_updated(cx);
self.pending_update_matches = Some(cx.spawn(|this, mut cx| async move {
update.await;
this.update(&mut cx, |this, cx| {
this.matches_updated(cx);
})
.ok()
}));
}
fn matches_updated(&mut self, cx: &mut ViewContext<Self>) {
let index = self.delegate.selected_index();
self.scroll_handle.scroll_to_item(index);
self.pending_update_matches = None;
cx.notify();
}
}
impl<D: PickerDelegate> Render for Picker<D> {
type Element = Div<Self, StatefulInteractivity<Self>, FocusEnabled<Self>>;
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
div()
.context("picker")
.id("picker-container")
.focusable()
.size_full()
.on_action(Self::select_next)
.on_action(Self::select_prev)
.on_action(Self::select_first)
.on_action(Self::select_last)
.on_action(Self::cancel)
.on_action(Self::confirm)
.on_action(Self::secondary_confirm)
.child(self.editor.clone())
.child(
uniform_list("candidates", self.delegate.match_count(), {
move |this: &mut Self, visible_range, cx| {
let selected_ix = this.delegate.selected_index();
visible_range
.map(|ix| this.delegate.render_match(ix, ix == selected_ix, cx))
.collect()
}
})
.track_scroll(self.scroll_handle.clone())
.size_full(),
)
}
}

View file

@ -1,7 +1,7 @@
use crate::{settings_store::parse_json_with_comments, SettingsAssets};
use anyhow::{anyhow, Context, Result};
use collections::BTreeMap;
use gpui::{AppContext, KeyBinding, SharedString};
use gpui::{actions, Action, AppContext, KeyBinding, SharedString};
use schemars::{
gen::{SchemaGenerator, SchemaSettings},
schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation},
@ -73,9 +73,9 @@ impl KeymapFile {
"Expected first item in array to be a string."
)));
};
cx.build_action(&name, Some(data))
gpui::build_action(&name, Some(data))
}
Value::String(name) => cx.build_action(&name, None),
Value::String(name) => gpui::build_action(&name, None),
Value::Null => Ok(no_action()),
_ => {
return Some(Err(anyhow!("Expected two-element array, got {action:?}")))
@ -137,8 +137,10 @@ impl KeymapFile {
}
}
actions!(NoAction);
fn no_action() -> Box<dyn gpui::Action> {
todo!()
NoAction.boxed_clone()
}
#[cfg(test)]

View file

@ -1,4 +1,4 @@
use crate::{settings_store::SettingsStore, Settings};
use crate::{settings_store::SettingsStore, KeymapFile, Settings};
use anyhow::Result;
use fs::Fs;
use futures::{channel::mpsc, StreamExt};
@ -117,3 +117,50 @@ pub fn update_settings_file<T: Settings>(
})
.detach_and_log_err(cx);
}
pub fn load_default_keymap(cx: &mut AppContext) {
for path in ["keymaps/default.json", "keymaps/vim.json"] {
KeymapFile::load_asset(path, cx).unwrap();
}
// todo!()
// if let Some(asset_path) = settings::get::<BaseKeymap>(cx).asset_path() {
// KeymapFile::load_asset(asset_path, cx).unwrap();
// }
}
pub fn handle_keymap_file_changes(
mut user_keymap_file_rx: mpsc::UnboundedReceiver<String>,
cx: &mut AppContext,
) {
cx.spawn(move |cx| async move {
// let mut settings_subscription = None;
while let Some(user_keymap_content) = user_keymap_file_rx.next().await {
if let Some(keymap_content) = KeymapFile::parse(&user_keymap_content).log_err() {
cx.update(|cx| reload_keymaps(cx, &keymap_content)).ok();
// todo!()
// let mut old_base_keymap = cx.read(|cx| *settings::get::<BaseKeymap>(cx));
// drop(settings_subscription);
// settings_subscription = Some(cx.update(|cx| {
// cx.observe_global::<SettingsStore, _>(move |cx| {
// let new_base_keymap = *settings::get::<BaseKeymap>(cx);
// if new_base_keymap != old_base_keymap {
// old_base_keymap = new_base_keymap.clone();
// reload_keymaps(cx, &keymap_content);
// }
// })
// }));
}
}
})
.detach();
}
fn reload_keymaps(cx: &mut AppContext, keymap_content: &KeymapFile) {
// todo!()
// cx.clear_bindings();
load_default_keymap(cx);
keymap_content.clone().add_to_cx(cx).log_err();
// cx.set_menus(menus::menus());
}

View file

@ -13,9 +13,12 @@ anyhow.workspace = true
# TODO: Remove after diagnosing stack overflow.
backtrace-on-stack-overflow = "0.3.0"
clap = { version = "4.4", features = ["derive", "string"] }
editor = { package = "editor2", path = "../editor2" }
chrono = "0.4"
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
gpui = { package = "gpui2", path = "../gpui2" }
itertools = "0.11.0"
language = { package = "language2", path = "../language2" }
log.workspace = true
rust-embed.workspace = true
serde.workspace = true
@ -25,8 +28,10 @@ smallvec.workspace = true
strum = { version = "0.25.0", features = ["derive"] }
theme = { path = "../theme" }
theme2 = { path = "../theme2" }
menu = { package = "menu2", path = "../menu2" }
ui = { package = "ui2", path = "../ui2", features = ["stories"] }
util = { path = "../util" }
picker = { package = "picker2", path = "../picker2" }
[dev-dependencies]
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }

View file

@ -1,6 +1,7 @@
mod colors;
mod focus;
mod kitchen_sink;
mod picker;
mod scroll;
mod text;
mod z_index;
@ -8,6 +9,7 @@ mod z_index;
pub use colors::*;
pub use focus::*;
pub use kitchen_sink::*;
pub use picker::*;
pub use scroll::*;
pub use text::*;
pub use z_index::*;

View file

@ -1,18 +1,10 @@
use gpui::{
div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteraction,
StatelessInteractive, Styled, View, VisualContext, WindowContext,
actions, div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render,
StatefulInteractivity, StatelessInteractive, Styled, View, VisualContext, WindowContext,
};
use serde::Deserialize;
use theme2::ActiveTheme;
#[derive(Clone, Default, PartialEq, Deserialize)]
struct ActionA;
#[derive(Clone, Default, PartialEq, Deserialize)]
struct ActionB;
#[derive(Clone, Default, PartialEq, Deserialize)]
struct ActionC;
actions!(ActionA, ActionB, ActionC);
pub struct FocusStory {}
@ -23,24 +15,22 @@ impl FocusStory {
KeyBinding::new("cmd-a", ActionB, Some("child-1")),
KeyBinding::new("cmd-c", ActionC, None),
]);
cx.register_action_type::<ActionA>();
cx.register_action_type::<ActionB>();
cx.build_view(move |cx| Self {})
}
}
impl Render for FocusStory {
type Element = Div<Self, StatefulInteraction<Self>, FocusEnabled<Self>>;
type Element = Div<Self, StatefulInteractivity<Self>, FocusEnabled<Self>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
let color_1 = theme.styles.git.created;
let color_2 = theme.styles.git.modified;
let color_3 = theme.styles.git.deleted;
let color_4 = theme.styles.git.conflict;
let color_5 = theme.styles.git.ignored;
let color_6 = theme.styles.git.renamed;
let color_1 = theme.status().created;
let color_2 = theme.status().modified;
let color_3 = theme.status().deleted;
let color_4 = theme.status().conflict;
let color_5 = theme.status().ignored;
let color_6 = theme.status().renamed;
let child_1 = cx.focus_handle();
let child_2 = cx.focus_handle();
@ -48,20 +38,18 @@ impl Render for FocusStory {
.id("parent")
.focusable()
.context("parent")
.on_action(|_, action: &ActionA, phase, cx| {
println!("Action A dispatched on parent during {:?}", phase);
.on_action(|_, action: &ActionA, cx| {
println!("Action A dispatched on parent during");
})
.on_action(|_, action: &ActionB, phase, cx| {
println!("Action B dispatched on parent during {:?}", phase);
.on_action(|_, action: &ActionB, cx| {
println!("Action B dispatched on parent during");
})
.on_focus(|_, _, _| println!("Parent focused"))
.on_blur(|_, _, _| println!("Parent blurred"))
.on_focus_in(|_, _, _| println!("Parent focus_in"))
.on_focus_out(|_, _, _| println!("Parent focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on parent {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| println!("Key up on parent {:?} {:?}", phase, event))
.on_key_down(|_, event, phase, _| println!("Key down on parent {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on parent {:?}", event))
.size_full()
.bg(color_1)
.focus(|style| style.bg(color_2))
@ -70,8 +58,8 @@ impl Render for FocusStory {
div()
.track_focus(&child_1)
.context("child-1")
.on_action(|_, action: &ActionB, phase, cx| {
println!("Action B dispatched on child 1 during {:?}", phase);
.on_action(|_, action: &ActionB, cx| {
println!("Action B dispatched on child 1 during");
})
.w_full()
.h_6()
@ -82,20 +70,16 @@ impl Render for FocusStory {
.on_blur(|_, _, _| println!("Child 1 blurred"))
.on_focus_in(|_, _, _| println!("Child 1 focus_in"))
.on_focus_out(|_, _, _| println!("Child 1 focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on child 1 {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| {
println!("Key up on child 1 {:?} {:?}", phase, event)
})
.on_key_down(|_, event, phase, _| println!("Key down on child 1 {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on child 1 {:?}", event))
.child("Child 1"),
)
.child(
div()
.track_focus(&child_2)
.context("child-2")
.on_action(|_, action: &ActionC, phase, cx| {
println!("Action C dispatched on child 2 during {:?}", phase);
.on_action(|_, action: &ActionC, cx| {
println!("Action C dispatched on child 2 during");
})
.w_full()
.h_6()
@ -104,12 +88,8 @@ impl Render for FocusStory {
.on_blur(|_, _, _| println!("Child 2 blurred"))
.on_focus_in(|_, _, _| println!("Child 2 focus_in"))
.on_focus_out(|_, _, _| println!("Child 2 focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on child 2 {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| {
println!("Key up on child 2 {:?} {:?}", phase, event)
})
.on_key_down(|_, event, phase, _| println!("Key down on child 2 {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on child 2 {:?}", event))
.child("Child 2"),
)
}

View file

@ -1,5 +1,5 @@
use crate::{story::Story, story_selector::ComponentStory};
use gpui::{Div, Render, StatefulInteraction, View, VisualContext};
use gpui::{Div, Render, StatefulInteractivity, View, VisualContext};
use strum::IntoEnumIterator;
use ui::prelude::*;
@ -12,7 +12,7 @@ impl KitchenSinkStory {
}
impl Render for KitchenSinkStory {
type Element = Div<Self, StatefulInteraction<Self>>;
type Element = Div<Self, StatefulInteractivity<Self>>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let component_stories = ComponentStory::iter()

View file

@ -0,0 +1,214 @@
use std::sync::Arc;
use fuzzy::StringMatchCandidate;
use gpui::{
div, Component, Div, KeyBinding, ParentElement, Render, StatelessInteractive, Styled, Task,
View, VisualContext, WindowContext,
};
use picker::{Picker, PickerDelegate};
use theme2::ActiveTheme;
pub struct PickerStory {
picker: View<Picker<Delegate>>,
}
struct Delegate {
candidates: Arc<[StringMatchCandidate]>,
matches: Vec<usize>,
selected_ix: usize,
}
impl Delegate {
fn new(strings: &[&str]) -> Self {
Self {
candidates: strings
.iter()
.copied()
.enumerate()
.map(|(id, string)| StringMatchCandidate {
id,
char_bag: string.into(),
string: string.into(),
})
.collect(),
matches: vec![],
selected_ix: 0,
}
}
}
impl PickerDelegate for Delegate {
type ListItem = Div<Picker<Self>>;
fn match_count(&self) -> usize {
self.candidates.len()
}
fn render_match(
&self,
ix: usize,
selected: bool,
cx: &mut gpui::ViewContext<Picker<Self>>,
) -> Self::ListItem {
let colors = cx.theme().colors();
let Some(candidate_ix) = self.matches.get(ix) else {
return div();
};
let candidate = self.candidates[*candidate_ix].string.clone();
div()
.text_color(colors.text)
.when(selected, |s| {
s.border_l_10().border_color(colors.terminal_ansi_yellow)
})
.hover(|style| {
style
.bg(colors.element_active)
.text_color(colors.text_accent)
})
.child(candidate)
}
fn selected_index(&self) -> usize {
self.selected_ix
}
fn set_selected_index(&mut self, ix: usize, cx: &mut gpui::ViewContext<Picker<Self>>) {
self.selected_ix = ix;
cx.notify();
}
fn confirm(&mut self, secondary: bool, cx: &mut gpui::ViewContext<Picker<Self>>) {
let candidate_ix = self.matches[self.selected_ix];
let candidate = self.candidates[candidate_ix].string.clone();
if secondary {
eprintln!("Secondary confirmed {}", candidate)
} else {
eprintln!("Confirmed {}", candidate)
}
}
fn dismissed(&mut self, cx: &mut gpui::ViewContext<Picker<Self>>) {
cx.quit();
}
fn update_matches(
&mut self,
query: String,
cx: &mut gpui::ViewContext<Picker<Self>>,
) -> Task<()> {
let candidates = self.candidates.clone();
self.matches = cx
.background_executor()
.block(fuzzy::match_strings(
&candidates,
&query,
true,
100,
&Default::default(),
cx.background_executor().clone(),
))
.into_iter()
.map(|r| r.candidate_id)
.collect();
self.selected_ix = 0;
Task::ready(())
}
}
impl PickerStory {
pub fn new(cx: &mut WindowContext) -> View<Self> {
cx.build_view(|cx| {
cx.bind_keys([
KeyBinding::new("up", menu::SelectPrev, Some("picker")),
KeyBinding::new("pageup", menu::SelectFirst, Some("picker")),
KeyBinding::new("shift-pageup", menu::SelectFirst, Some("picker")),
KeyBinding::new("ctrl-p", menu::SelectPrev, Some("picker")),
KeyBinding::new("down", menu::SelectNext, Some("picker")),
KeyBinding::new("pagedown", menu::SelectLast, Some("picker")),
KeyBinding::new("shift-pagedown", menu::SelectFirst, Some("picker")),
KeyBinding::new("ctrl-n", menu::SelectNext, Some("picker")),
KeyBinding::new("cmd-up", menu::SelectFirst, Some("picker")),
KeyBinding::new("cmd-down", menu::SelectLast, Some("picker")),
KeyBinding::new("enter", menu::Confirm, Some("picker")),
KeyBinding::new("ctrl-enter", menu::ShowContextMenu, Some("picker")),
KeyBinding::new("cmd-enter", menu::SecondaryConfirm, Some("picker")),
KeyBinding::new("escape", menu::Cancel, Some("picker")),
KeyBinding::new("ctrl-c", menu::Cancel, Some("picker")),
]);
PickerStory {
picker: cx.build_view(|cx| {
let mut delegate = Delegate::new(&[
"Baguette (France)",
"Baklava (Turkey)",
"Beef Wellington (UK)",
"Biryani (India)",
"Borscht (Ukraine)",
"Bratwurst (Germany)",
"Bulgogi (Korea)",
"Burrito (USA)",
"Ceviche (Peru)",
"Chicken Tikka Masala (India)",
"Churrasco (Brazil)",
"Couscous (North Africa)",
"Croissant (France)",
"Dim Sum (China)",
"Empanada (Argentina)",
"Fajitas (Mexico)",
"Falafel (Middle East)",
"Feijoada (Brazil)",
"Fish and Chips (UK)",
"Fondue (Switzerland)",
"Goulash (Hungary)",
"Haggis (Scotland)",
"Kebab (Middle East)",
"Kimchi (Korea)",
"Lasagna (Italy)",
"Maple Syrup Pancakes (Canada)",
"Moussaka (Greece)",
"Pad Thai (Thailand)",
"Paella (Spain)",
"Pancakes (USA)",
"Pasta Carbonara (Italy)",
"Pavlova (Australia)",
"Peking Duck (China)",
"Pho (Vietnam)",
"Pierogi (Poland)",
"Pizza (Italy)",
"Poutine (Canada)",
"Pretzel (Germany)",
"Ramen (Japan)",
"Rendang (Indonesia)",
"Sashimi (Japan)",
"Satay (Indonesia)",
"Shepherd's Pie (Ireland)",
"Sushi (Japan)",
"Tacos (Mexico)",
"Tandoori Chicken (India)",
"Tortilla (Spain)",
"Tzatziki (Greece)",
"Wiener Schnitzel (Austria)",
]);
delegate.update_matches("".into(), cx).detach();
let picker = Picker::new(delegate, cx);
picker.focus(cx);
picker
}),
}
})
}
}
impl Render for PickerStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div()
.bg(cx.theme().styles.colors.background)
.size_full()
.child(self.picker.clone())
}
}

View file

@ -1,5 +1,5 @@
use gpui::{
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled,
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteractivity, Styled,
View, VisualContext, WindowContext,
};
use theme2::ActiveTheme;
@ -13,12 +13,12 @@ impl ScrollStory {
}
impl Render for ScrollStory {
type Element = Div<Self, StatefulInteraction<Self>>;
type Element = Div<Self, StatefulInteractivity<Self>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
let color_1 = theme.styles.git.created;
let color_2 = theme.styles.git.modified;
let color_1 = theme.status().created;
let color_2 = theme.status().modified;
div()
.id("parent")

View file

@ -38,6 +38,7 @@ pub enum ComponentStory {
Palette,
Panel,
ProjectPanel,
Players,
RecentProjects,
Scroll,
Tab,
@ -51,6 +52,7 @@ pub enum ComponentStory {
TrafficLights,
Workspace,
ZIndex,
Picker,
}
impl ComponentStory {
@ -79,6 +81,7 @@ impl ComponentStory {
Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into(),
Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into(),
Self::Palette => cx.build_view(|cx| ui::PaletteStory).into(),
Self::Players => cx.build_view(|_| theme2::PlayerStory).into(),
Self::Panel => cx.build_view(|cx| ui::PanelStory).into(),
Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into(),
Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into(),
@ -94,6 +97,7 @@ impl ComponentStory {
Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into(),
Self::Workspace => ui::WorkspaceStory::view(cx).into(),
Self::ZIndex => cx.build_view(|_| ZIndexStory).into(),
Self::Picker => PickerStory::new(cx).into(),
}
}
}

View file

@ -72,6 +72,8 @@ fn main() {
ThemeSettings::override_global(theme_settings, cx);
ui::settings::init(cx);
language::init(cx);
editor::init(cx);
let window = cx.open_window(
WindowOptions {

View file

@ -186,9 +186,9 @@ pub fn mouse_side(
}
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
let col = GridCol((pos.x / cur_size.cell_width).as_usize());
let col = GridCol((cur_size.cell_width / pos.x) as usize);
let col = min(col, cur_size.last_column());
let line = (pos.y / cur_size.line_height).as_isize() as i32;
let line = (cur_size.line_height / pos.y) as i32;
let line = min(line, cur_size.bottommost_line().0);
AlacPoint::new(GridLine(line - display_offset as i32), col)
}

View file

@ -1121,8 +1121,7 @@ impl Terminal {
None => return,
};
let scroll_lines =
(scroll_delta / self.last_content.size.line_height).as_isize() as i32;
let scroll_lines = (scroll_delta / self.last_content.size.line_height) as i32;
self.events
.push_back(InternalEvent::Scroll(AlacScroll::Delta(scroll_lines)));
@ -1280,11 +1279,11 @@ impl Terminal {
}
/* Calculate the appropriate scroll lines */
TouchPhase::Moved => {
let old_offset = (self.scroll_px / line_height).as_isize() as i32;
let old_offset = (self.scroll_px / line_height) as i32;
self.scroll_px += e.delta.pixel_delta(line_height).y * scroll_multiplier;
let new_offset = (self.scroll_px / line_height).as_isize() as i32;
let new_offset = (self.scroll_px / line_height) as i32;
// Whenever we hit the edges, reset our stored scroll to 0
// so we can respond to changes in direction quickly
@ -1396,9 +1395,9 @@ fn all_search_matches<'a, T>(
}
fn content_index_for_mouse(pos: Point<Pixels>, size: &TerminalSize) -> usize {
let col = (pos.x / size.cell_width()).round().as_usize();
let col = (pos.x / size.cell_width()).round() as usize;
let clamped_col = min(col, size.columns() - 1);
let row = (pos.y / size.line_height()).round().as_usize();
let row = (pos.y / size.line_height()).round() as usize;
let clamped_row = min(row, size.screen_lines() - 1);
clamped_row * size.columns() + clamped_col
}

View file

@ -5,6 +5,8 @@ edition = "2021"
publish = false
[features]
default = ["stories"]
stories = ["dep:itertools"]
test-support = [
"gpui/test-support",
"fs/test-support",
@ -28,7 +30,9 @@ serde_derive.workspace = true
serde_json.workspace = true
settings = { package = "settings2", path = "../settings2" }
toml.workspace = true
uuid.workspace = true
util = { path = "../util" }
itertools = { version = "0.11.0", optional = true }
[dev-dependencies]
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }

View file

@ -1,7 +1,9 @@
use std::sync::Arc;
use gpui::Hsla;
use refineable::Refineable;
use crate::SyntaxTheme;
use crate::{PlayerColors, SyntaxTheme};
#[derive(Clone)]
pub struct SystemColors {
@ -11,16 +13,6 @@ pub struct SystemColors {
pub mac_os_traffic_light_green: Hsla,
}
#[derive(Debug, Clone, Copy)]
pub struct PlayerColor {
pub cursor: Hsla,
pub background: Hsla,
pub selection: Hsla,
}
#[derive(Clone)]
pub struct PlayerColors(pub Vec<PlayerColor>);
#[derive(Refineable, Clone, Debug)]
#[refineable(debug)]
pub struct StatusColors {
@ -37,77 +29,217 @@ pub struct StatusColors {
pub warning: Hsla,
}
#[derive(Refineable, Clone, Debug)]
#[refineable(debug)]
pub struct GitStatusColors {
pub conflict: Hsla,
pub created: Hsla,
pub deleted: Hsla,
pub ignored: Hsla,
pub modified: Hsla,
pub renamed: Hsla,
}
#[derive(Refineable, Clone, Debug)]
#[refineable(debug, deserialize)]
pub struct ThemeColors {
pub border: Hsla,
/// Border color. Used for deemphasized borders, like a visual divider between two sections
pub border_variant: Hsla,
/// Border color. Used for focused elements, like keyboard focused list item.
pub border_focused: Hsla,
/// Border color. Used for selected elements, like an active search filter or selected checkbox.
pub border_selected: Hsla,
/// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change.
pub border_transparent: Hsla,
/// Border color. Used for disabled elements, like a disabled input or button.
pub border_disabled: Hsla,
/// Border color. Used for elevated surfaces, like a context menu, popup, or dialog.
pub elevated_surface_background: Hsla,
/// Background Color. Used for grounded surfaces like a panel or tab.
pub surface_background: Hsla,
/// Background Color. Used for the app background and blank panels or windows.
pub background: Hsla,
/// Background Color. Used for the background of an element that should have a different background than the surface it's on.
///
/// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
///
/// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
pub element_background: Hsla,
/// Background Color. Used for the hover state of an element that should have a different background than the surface it's on.
///
/// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
pub element_hover: Hsla,
/// Background Color. Used for the active state of an element that should have a different background than the surface it's on.
///
/// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
pub element_active: Hsla,
/// Background Color. Used for the selected state of an element that should have a different background than the surface it's on.
///
/// Selected states are triggered by the element being selected (or "activated") by the user.
///
/// This could include a selected checkbox, a toggleable button that is toggled on, etc.
pub element_selected: Hsla,
/// Background Color. Used for the disabled state of an element that should have a different background than the surface it's on.
///
/// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
pub element_disabled: Hsla,
pub element_placeholder: Hsla,
pub element_drop_target: Hsla,
/// Background Color. Used for the area that shows where a dragged element will be dropped.
pub drop_target_background: Hsla,
/// Border Color. Used to show the area that shows where a dragged element will be dropped.
// pub drop_target_border: Hsla,
/// Used for the background of a ghost element that should have the same background as the surface it's on.
///
/// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
///
/// For an element that should have a different background than the surface it's on, use `element_background`.
pub ghost_element_background: Hsla,
/// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on.
///
/// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
pub ghost_element_hover: Hsla,
/// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on.
///
/// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressd.
pub ghost_element_active: Hsla,
/// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on.
///
/// Selected states are triggered by the element being selected (or "activated") by the user.
///
/// This could include a selected checkbox, a toggleable button that is toggled on, etc.
pub ghost_element_selected: Hsla,
/// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on.
///
/// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
pub ghost_element_disabled: Hsla,
/// Text Color. Default text color used for most text.
pub text: Hsla,
/// Text Color. Color of muted or deemphasized text. It is a subdued version of the standard text color.
pub text_muted: Hsla,
/// Text Color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data.
pub text_placeholder: Hsla,
/// Text Color. Color used for text denoting disabled elements. Typically, the color is faded or grayed out to emphasize the disabled state.
pub text_disabled: Hsla,
/// Text Color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search.
pub text_accent: Hsla,
/// Fill Color. Used for the default fill color of an icon.
pub icon: Hsla,
/// Fill Color. Used for the muted or deemphasized fill color of an icon.
///
/// This might be used to show an icon in an inactive pane, or to demphasize a series of icons to give them less visual weight.
pub icon_muted: Hsla,
/// Fill Color. Used for the disabled fill color of an icon.
///
/// Disabled states are shown when a user cannot interact with an element, like a icon button.
pub icon_disabled: Hsla,
/// Fill Color. Used for the placeholder fill color of an icon.
///
/// This might be used to show an icon in an input that disappears when the user enters text.
pub icon_placeholder: Hsla,
/// Fill Color. Used for the accent fill color of an icon.
///
/// This might be used to show when a toggleable icon button is selected.
pub icon_accent: Hsla,
// ===
// UI Elements
// ===
pub status_bar_background: Hsla,
pub title_bar_background: Hsla,
pub toolbar_background: Hsla,
pub tab_bar_background: Hsla,
pub tab_inactive_background: Hsla,
pub tab_active_background: Hsla,
// pub panel_background: Hsla,
// pub pane_focused_border: Hsla,
// /// The color of the scrollbar thumb.
// pub scrollbar_thumb_background: Hsla,
// /// The color of the scrollbar thumb when hovered over.
// pub scrollbar_thumb_hover_background: Hsla,
// /// The border color of the scrollbar thumb.
// pub scrollbar_thumb_border: Hsla,
// /// The background color of the scrollbar track.
// pub scrollbar_track_background: Hsla,
// /// The border color of the scrollbar track.
// pub scrollbar_track_border: Hsla,
// /// The opacity of the scrollbar status marks, like diagnostic states and git status..
// pub scrollbar_status_opacity: Hsla,
// ===
// Editor
// ===
pub editor_background: Hsla,
// pub editor_inactive_background: Hsla,
pub editor_gutter_background: Hsla,
pub editor_subheader_background: Hsla,
pub editor_active_line: Hsla,
pub editor_active_line_background: Hsla,
pub editor_highlighted_line_background: Hsla,
/// Text Color. Used for the text of the line number in the editor gutter.
pub editor_line_number: Hsla,
/// Text Color. Used for the text of the line number in the editor gutter when the line is highlighted.
pub editor_active_line_number: Hsla,
/// Text Color. Used to mark invisible characters in the editor.
///
/// Example: spaces, tabs, carriage returns, etc.
pub editor_invisible: Hsla,
pub editor_wrap_guide: Hsla,
pub editor_active_wrap_guide: Hsla,
pub editor_document_highlight_read_background: Hsla,
pub editor_document_highlight_write_background: Hsla,
// ===
// Terminal
// ===
/// Terminal Background Color
pub terminal_background: Hsla,
/// Bright Black Color for ANSI Terminal
pub terminal_ansi_bright_black: Hsla,
/// Bright Red Color for ANSI Terminal
pub terminal_ansi_bright_red: Hsla,
/// Bright Green Color for ANSI Terminal
pub terminal_ansi_bright_green: Hsla,
/// Bright Yellow Color for ANSI Terminal
pub terminal_ansi_bright_yellow: Hsla,
/// Bright Blue Color for ANSI Terminal
pub terminal_ansi_bright_blue: Hsla,
/// Bright Magenta Color for ANSI Terminal
pub terminal_ansi_bright_magenta: Hsla,
/// Bright Cyan Color for ANSI Terminal
pub terminal_ansi_bright_cyan: Hsla,
/// Bright White Color for ANSI Terminal
pub terminal_ansi_bright_white: Hsla,
/// Black Color for ANSI Terminal
pub terminal_ansi_black: Hsla,
/// Red Color for ANSI Terminal
pub terminal_ansi_red: Hsla,
/// Green Color for ANSI Terminal
pub terminal_ansi_green: Hsla,
/// Yellow Color for ANSI Terminal
pub terminal_ansi_yellow: Hsla,
/// Blue Color for ANSI Terminal
pub terminal_ansi_blue: Hsla,
/// Magenta Color for ANSI Terminal
pub terminal_ansi_magenta: Hsla,
/// Cyan Color for ANSI Terminal
pub terminal_ansi_cyan: Hsla,
/// White Color for ANSI Terminal
pub terminal_ansi_white: Hsla,
// new colors
// ===
// Elevation
// ===
// elevation_0_shadow
// elevation_0_shadow_color
// elevation_1_shadow
// elevation_1_shadow_color
// elevation_2_shadow
// elevation_2_shadow_color
// elevation_3_shadow
// elevation_3_shadow_color
// elevation_4_shadow
// elevation_4_shadow_color
// elevation_5_shadow
// elevation_5_shadow_color
// ===
// UI Text
// ===
// pub headline: Hsla,
// pub paragraph: Hsla,
// pub link: Hsla,
// pub link_hover: Hsla,
// pub code_block_background: Hsla,
// pub code_block_border: Hsla,
}
#[derive(Refineable, Clone)]
@ -117,9 +249,8 @@ pub struct ThemeStyles {
#[refineable]
pub colors: ThemeColors,
pub status: StatusColors,
pub git: GitStatusColors,
pub player: PlayerColors,
pub syntax: SyntaxTheme,
pub syntax: Arc<SyntaxTheme>,
}
#[cfg(test)]

View file

@ -2,12 +2,104 @@ use std::num::ParseIntError;
use gpui::{hsla, Hsla, Rgba};
use crate::{
colors::{GitStatusColors, PlayerColor, PlayerColors, StatusColors, SystemColors, ThemeColors},
scale::{ColorScaleSet, ColorScales},
syntax::SyntaxTheme,
ColorScale,
};
use crate::colors::{StatusColors, SystemColors, ThemeColors};
use crate::scale::{ColorScaleSet, ColorScales};
use crate::syntax::SyntaxTheme;
use crate::{ColorScale, PlayerColor, PlayerColors};
impl Default for PlayerColors {
fn default() -> Self {
Self(vec![
PlayerColor {
cursor: blue().dark().step_9(),
background: blue().dark().step_5(),
selection: blue().dark().step_3(),
},
PlayerColor {
cursor: orange().dark().step_9(),
background: orange().dark().step_5(),
selection: orange().dark().step_3(),
},
PlayerColor {
cursor: pink().dark().step_9(),
background: pink().dark().step_5(),
selection: pink().dark().step_3(),
},
PlayerColor {
cursor: lime().dark().step_9(),
background: lime().dark().step_5(),
selection: lime().dark().step_3(),
},
PlayerColor {
cursor: purple().dark().step_9(),
background: purple().dark().step_5(),
selection: purple().dark().step_3(),
},
PlayerColor {
cursor: amber().dark().step_9(),
background: amber().dark().step_5(),
selection: amber().dark().step_3(),
},
PlayerColor {
cursor: jade().dark().step_9(),
background: jade().dark().step_5(),
selection: jade().dark().step_3(),
},
PlayerColor {
cursor: red().dark().step_9(),
background: red().dark().step_5(),
selection: red().dark().step_3(),
},
])
}
}
impl PlayerColors {
pub fn default_light() -> Self {
Self(vec![
PlayerColor {
cursor: blue().light().step_9(),
background: blue().light().step_4(),
selection: blue().light().step_3(),
},
PlayerColor {
cursor: orange().light().step_9(),
background: orange().light().step_4(),
selection: orange().light().step_3(),
},
PlayerColor {
cursor: pink().light().step_9(),
background: pink().light().step_4(),
selection: pink().light().step_3(),
},
PlayerColor {
cursor: lime().light().step_9(),
background: lime().light().step_4(),
selection: lime().light().step_3(),
},
PlayerColor {
cursor: purple().light().step_9(),
background: purple().light().step_4(),
selection: purple().light().step_3(),
},
PlayerColor {
cursor: amber().light().step_9(),
background: amber().light().step_4(),
selection: amber().light().step_3(),
},
PlayerColor {
cursor: jade().light().step_9(),
background: jade().light().step_4(),
selection: jade().light().step_3(),
},
PlayerColor {
cursor: red().light().step_9(),
background: red().light().step_4(),
selection: red().light().step_3(),
},
])
}
}
fn neutral() -> ColorScaleSet {
slate()
@ -27,61 +119,21 @@ impl Default for SystemColors {
impl Default for StatusColors {
fn default() -> Self {
Self {
conflict: red().dark().step_11(),
created: grass().dark().step_11(),
deleted: red().dark().step_11(),
error: red().dark().step_11(),
hidden: neutral().dark().step_11(),
ignored: neutral().dark().step_11(),
info: blue().dark().step_11(),
modified: yellow().dark().step_11(),
renamed: blue().dark().step_11(),
success: grass().dark().step_11(),
warning: yellow().dark().step_11(),
conflict: red().dark().step_9(),
created: grass().dark().step_9(),
deleted: red().dark().step_9(),
error: red().dark().step_9(),
hidden: neutral().dark().step_9(),
ignored: neutral().dark().step_9(),
info: blue().dark().step_9(),
modified: yellow().dark().step_9(),
renamed: blue().dark().step_9(),
success: grass().dark().step_9(),
warning: yellow().dark().step_9(),
}
}
}
impl Default for GitStatusColors {
fn default() -> Self {
Self {
conflict: orange().dark().step_11(),
created: grass().dark().step_11(),
deleted: red().dark().step_11(),
ignored: neutral().dark().step_11(),
modified: yellow().dark().step_11(),
renamed: blue().dark().step_11(),
}
}
}
impl Default for PlayerColors {
fn default() -> Self {
Self(vec![
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 0.0),
background: hsla(0.0, 0.0, 0.0, 0.0),
selection: hsla(0.0, 0.0, 0.0, 0.0),
},
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 0.0),
background: hsla(0.0, 0.0, 0.0, 0.0),
selection: hsla(0.0, 0.0, 0.0, 0.0),
},
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 0.0),
background: hsla(0.0, 0.0, 0.0, 0.0),
selection: hsla(0.0, 0.0, 0.0, 0.0),
},
PlayerColor {
cursor: hsla(0.0, 0.0, 0.0, 0.0),
background: hsla(0.0, 0.0, 0.0, 0.0),
selection: hsla(0.0, 0.0, 0.0, 0.0),
},
])
}
}
impl SyntaxTheme {
pub fn default_light() -> Self {
Self {
@ -138,6 +190,8 @@ impl SyntaxTheme {
("variable.special".into(), red().light().step_7().into()),
("variant".into(), red().light().step_7().into()),
],
inlay_style: tomato().light().step_1().into(), // todo!("nate: use a proper style")
suggestion_style: orange().light().step_1().into(), // todo!("nate: use proper style")
}
}
@ -193,6 +247,8 @@ impl SyntaxTheme {
("variable.special".into(), red().dark().step_7().into()),
("variant".into(), red().dark().step_7().into()),
],
inlay_style: tomato().dark().step_1().into(), // todo!("nate: use a proper style")
suggestion_style: orange().dark().step_1().into(), // todo!("nate: use a proper style")
}
}
}
@ -216,8 +272,7 @@ impl ThemeColors {
element_active: neutral().light().step_5(),
element_selected: neutral().light().step_5(),
element_disabled: neutral().light_alpha().step_3(),
element_placeholder: neutral().light().step_11(),
element_drop_target: blue().light_alpha().step_2(),
drop_target_background: blue().light_alpha().step_2(),
ghost_element_background: system.transparent,
ghost_element_hover: neutral().light().step_4(),
ghost_element_active: neutral().light().step_5(),
@ -240,8 +295,17 @@ impl ThemeColors {
tab_active_background: neutral().light().step_1(),
tab_inactive_background: neutral().light().step_2(),
editor_background: neutral().light().step_1(),
editor_gutter_background: neutral().light().step_1(), // todo!("pick the right colors")
editor_subheader_background: neutral().light().step_2(),
editor_active_line: neutral().light_alpha().step_3(),
editor_active_line_background: neutral().light_alpha().step_3(),
editor_line_number: neutral().light_alpha().step_3(), // todo!("pick the right colors")
editor_active_line_number: neutral().light_alpha().step_3(), // todo!("pick the right colors")
editor_highlighted_line_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
editor_invisible: neutral().light_alpha().step_4(), // todo!("pick the right colors")
editor_wrap_guide: neutral().light_alpha().step_4(), // todo!("pick the right colors")
editor_active_wrap_guide: neutral().light_alpha().step_4(), // todo!("pick the right colors")
editor_document_highlight_read_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
editor_document_highlight_write_background: neutral().light_alpha().step_4(), // todo!("pick the right colors")
terminal_background: neutral().light().step_1(),
terminal_ansi_black: black().light().step_12(),
terminal_ansi_red: red().light().step_11(),
@ -280,8 +344,7 @@ impl ThemeColors {
element_active: neutral().dark().step_5(),
element_selected: neutral().dark().step_5(),
element_disabled: neutral().dark_alpha().step_3(),
element_placeholder: neutral().dark().step_11(),
element_drop_target: blue().dark_alpha().step_2(),
drop_target_background: blue().dark_alpha().step_2(),
ghost_element_background: system.transparent,
ghost_element_hover: neutral().dark().step_4(),
ghost_element_active: neutral().dark().step_5(),
@ -304,8 +367,17 @@ impl ThemeColors {
tab_active_background: neutral().dark().step_1(),
tab_inactive_background: neutral().dark().step_2(),
editor_background: neutral().dark().step_1(),
editor_gutter_background: neutral().dark().step_1(), // todo!("pick the right colors")
editor_subheader_background: neutral().dark().step_2(),
editor_active_line: neutral().dark_alpha().step_3(),
editor_active_line_background: neutral().dark_alpha().step_3(),
editor_line_number: neutral().dark_alpha().step_3(), // todo!("pick the right colors")
editor_active_line_number: neutral().dark_alpha().step_3(), // todo!("pick the right colors")
editor_highlighted_line_background: neutral().dark_alpha().step_4(), // todo!("pick the right colors")
editor_invisible: neutral().dark_alpha().step_4(), // todo!("pick the right colors")
editor_wrap_guide: neutral().dark_alpha().step_4(), // todo!("pick the right colors")
editor_active_wrap_guide: neutral().dark_alpha().step_4(), // todo!("pick the right colors")
editor_document_highlight_read_background: neutral().dark_alpha().step_4(), // todo!("pick the right colors")
editor_document_highlight_write_background: neutral().dark_alpha().step_4(), // todo!("pick the right colors")
terminal_background: neutral().dark().step_1(),
terminal_ansi_black: black().dark().step_12(),
terminal_ansi_red: red().dark().step_11(),

View file

@ -1,10 +1,12 @@
use std::sync::Arc;
use crate::{
colors::{GitStatusColors, PlayerColors, StatusColors, SystemColors, ThemeColors, ThemeStyles},
default_color_scales, Appearance, SyntaxTheme, ThemeFamily, ThemeVariant,
colors::{StatusColors, SystemColors, ThemeColors, ThemeStyles},
default_color_scales, Appearance, PlayerColors, SyntaxTheme, Theme, ThemeFamily,
};
fn zed_pro_daylight() -> ThemeVariant {
ThemeVariant {
fn zed_pro_daylight() -> Theme {
Theme {
id: "zed_pro_daylight".to_string(),
name: "Zed Pro Daylight".into(),
appearance: Appearance::Light,
@ -12,15 +14,14 @@ fn zed_pro_daylight() -> ThemeVariant {
system: SystemColors::default(),
colors: ThemeColors::default_light(),
status: StatusColors::default(),
git: GitStatusColors::default(),
player: PlayerColors::default(),
syntax: SyntaxTheme::default_light(),
player: PlayerColors::default_light(),
syntax: Arc::new(SyntaxTheme::default_light()),
},
}
}
pub(crate) fn zed_pro_moonlight() -> ThemeVariant {
ThemeVariant {
pub(crate) fn zed_pro_moonlight() -> Theme {
Theme {
id: "zed_pro_moonlight".to_string(),
name: "Zed Pro Moonlight".into(),
appearance: Appearance::Dark,
@ -28,9 +29,8 @@ pub(crate) fn zed_pro_moonlight() -> ThemeVariant {
system: SystemColors::default(),
colors: ThemeColors::default_dark(),
status: StatusColors::default(),
git: GitStatusColors::default(),
player: PlayerColors::default(),
syntax: SyntaxTheme::default_dark(),
syntax: Arc::new(SyntaxTheme::default_dark()),
},
}
}
@ -51,7 +51,7 @@ impl Default for ThemeFamily {
}
}
impl Default for ThemeVariant {
impl Default for Theme {
fn default() -> Self {
zed_pro_daylight()
}

View file

@ -0,0 +1,170 @@
use gpui::Hsla;
#[derive(Debug, Clone, Copy)]
pub struct PlayerColor {
pub cursor: Hsla,
pub background: Hsla,
pub selection: Hsla,
}
/// A collection of colors that are used to color players in the editor.
///
/// The first color is always the local player's color, usually a blue.
///
/// The rest of the default colors crisscross back and forth on the
/// color wheel so that the colors are as distinct as possible.
#[derive(Clone)]
pub struct PlayerColors(pub Vec<PlayerColor>);
impl PlayerColors {
pub fn local(&self) -> PlayerColor {
// todo!("use a valid color");
*self.0.first().unwrap()
}
pub fn absent(&self) -> PlayerColor {
// todo!("use a valid color");
*self.0.last().unwrap()
}
pub fn color_for_participant(&self, participant_index: u32) -> PlayerColor {
let len = self.0.len() - 1;
self.0[(participant_index as usize % len) + 1]
}
}
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
use super::*;
use crate::{ActiveTheme, Story};
use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
pub struct PlayerStory;
impl Render for PlayerStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx).child(
div()
.flex()
.flex_col()
.gap_4()
.child(Story::title_for::<_, PlayerColors>(cx))
.child(Story::label(cx, "Player Colors"))
.child(
div()
.flex()
.flex_col()
.gap_1()
.child(
div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div().w_8().h_8().rounded_md().bg(player.cursor)
}),
),
)
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div().w_8().h_8().rounded_md().bg(player.background)
}),
))
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div().w_8().h_8().rounded_md().bg(player.selection)
}),
)),
)
.child(Story::label(cx, "Avatar Rings"))
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div()
.my_1()
.rounded_full()
.border_2()
.border_color(player.cursor)
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size_6()
.bg(gpui::red()),
)
}),
))
.child(Story::label(cx, "Player Backgrounds"))
.child(div().flex().gap_1().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div()
.my_1()
.rounded_xl()
.flex()
.items_center()
.h_8()
.py_0p5()
.px_1p5()
.bg(player.background)
.child(
div().relative().neg_mx_1().rounded_full().z_index(3)
.border_2()
.border_color(player.background)
.size(px(28.))
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size(px(24.))
.bg(gpui::red()),
),
).child(
div().relative().neg_mx_1().rounded_full().z_index(2)
.border_2()
.border_color(player.background)
.size(px(28.))
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size(px(24.))
.bg(gpui::red()),
),
).child(
div().relative().neg_mx_1().rounded_full().z_index(1)
.border_2()
.border_color(player.background)
.size(px(28.))
.child(
img()
.rounded_full()
.uri("https://avatars.githubusercontent.com/u/1714999?v=4")
.size(px(24.))
.bg(gpui::red()),
),
)
}),
))
.child(Story::label(cx, "Player Selections"))
.child(div().flex().flex_col().gap_px().children(
cx.theme().players().0.clone().iter_mut().map(|player| {
div()
.flex()
.child(
div()
.flex()
.flex_none()
.rounded_sm()
.px_0p5()
.text_color(cx.theme().colors().text)
.bg(player.selection)
.child("The brown fox jumped over the lazy dog."),
)
.child(div().flex_1())
}),
)),
)
}
}
}

View file

@ -1,10 +1,17 @@
use crate::{zed_pro_family, ThemeFamily, ThemeVariant};
use std::collections::HashMap;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use gpui::SharedString;
use std::{collections::HashMap, sync::Arc};
use refineable::Refineable;
use crate::{
zed_pro_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
};
pub struct ThemeRegistry {
themes: HashMap<SharedString, Arc<ThemeVariant>>,
themes: HashMap<SharedString, Arc<Theme>>,
}
impl ThemeRegistry {
@ -14,12 +21,45 @@ impl ThemeRegistry {
}
}
fn insert_themes(&mut self, themes: impl IntoIterator<Item = ThemeVariant>) {
fn insert_themes(&mut self, themes: impl IntoIterator<Item = Theme>) {
for theme in themes.into_iter() {
self.themes.insert(theme.name.clone(), Arc::new(theme));
}
}
fn insert_user_theme_familes(&mut self, families: impl IntoIterator<Item = UserThemeFamily>) {
for family in families.into_iter() {
self.insert_user_themes(family.themes);
}
}
fn insert_user_themes(&mut self, themes: impl IntoIterator<Item = UserTheme>) {
self.insert_themes(themes.into_iter().map(|user_theme| {
let mut theme_colors = match user_theme.appearance {
Appearance::Light => ThemeColors::default_light(),
Appearance::Dark => ThemeColors::default_dark(),
};
theme_colors.refine(&user_theme.styles.colors);
Theme {
id: uuid::Uuid::new_v4().to_string(),
name: user_theme.name.into(),
appearance: user_theme.appearance,
styles: ThemeStyles {
system: SystemColors::default(),
colors: theme_colors,
status: StatusColors::default(),
player: PlayerColors::default(),
syntax: match user_theme.appearance {
Appearance::Light => Arc::new(SyntaxTheme::default_light()),
Appearance::Dark => Arc::new(SyntaxTheme::default_dark()),
},
},
}
}));
}
pub fn list_names(&self, _staff: bool) -> impl Iterator<Item = SharedString> + '_ {
self.themes.keys().cloned()
}
@ -28,7 +68,7 @@ impl ThemeRegistry {
self.themes.values().map(|theme| theme.name.clone())
}
pub fn get(&self, name: &str) -> Result<Arc<ThemeVariant>> {
pub fn get(&self, name: &str) -> Result<Arc<Theme>> {
self.themes
.get(name)
.ok_or_else(|| anyhow!("theme not found: {}", name))
@ -43,6 +83,7 @@ impl Default for ThemeRegistry {
};
this.insert_theme_families([zed_pro_family()]);
this.insert_user_theme_familes(crate::all_user_themes());
this
}

View file

@ -1,4 +1,4 @@
use crate::{ThemeRegistry, ThemeVariant};
use crate::{Theme, ThemeRegistry};
use anyhow::Result;
use gpui::{px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels};
use schemars::{
@ -21,7 +21,7 @@ pub struct ThemeSettings {
pub buffer_font: Font,
pub buffer_font_size: Pixels,
pub buffer_line_height: BufferLineHeight,
pub active_theme: Arc<ThemeVariant>,
pub active_theme: Arc<Theme>,
}
#[derive(Default)]

View file

@ -0,0 +1,38 @@
use gpui::{div, Component, Div, ParentElement, Styled, ViewContext};
use crate::ActiveTheme;
pub struct Story {}
impl Story {
pub fn container<V: 'static>(cx: &mut ViewContext<V>) -> Div<V> {
div()
.size_full()
.flex()
.flex_col()
.pt_2()
.px_4()
.font("Zed Mono")
.bg(cx.theme().colors().background)
}
pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: &str) -> impl Component<V> {
div()
.text_xl()
.text_color(cx.theme().colors().text)
.child(title.to_owned())
}
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Component<V> {
Self::title(cx, std::any::type_name::<T>())
}
pub fn label<V: 'static>(cx: &mut ViewContext<V>, label: &str) -> impl Component<V> {
div()
.mt_4()
.mb_2()
.text_xs()
.text_color(cx.theme().colors().text)
.child(label.to_owned())
}
}

View file

@ -3,6 +3,8 @@ use gpui::{HighlightStyle, Hsla};
#[derive(Clone, Default)]
pub struct SyntaxTheme {
pub highlights: Vec<(String, HighlightStyle)>,
pub inlay_style: HighlightStyle,
pub suggestion_style: HighlightStyle,
}
impl SyntaxTheme {
@ -21,6 +23,8 @@ impl SyntaxTheme {
)
})
.collect(),
inlay_style: HighlightStyle::default(),
suggestion_style: HighlightStyle::default(),
}
}

View file

@ -1,23 +1,32 @@
mod colors;
mod default_colors;
mod default_theme;
mod players;
mod registry;
mod scale;
mod settings;
mod syntax;
mod themes;
mod user_theme;
use std::sync::Arc;
use ::settings::Settings;
pub use colors::*;
pub use default_colors::*;
pub use default_theme::*;
pub use players::*;
pub use registry::*;
pub use scale::*;
pub use settings::*;
pub use syntax::*;
pub use themes::*;
pub use user_theme::*;
use gpui::{AppContext, Hsla, SharedString};
use serde::Deserialize;
#[derive(Debug, PartialEq, Clone, Copy)]
#[derive(Debug, PartialEq, Clone, Copy, Deserialize)]
pub enum Appearance {
Light,
Dark,
@ -29,35 +38,45 @@ pub fn init(cx: &mut AppContext) {
}
pub trait ActiveTheme {
fn theme(&self) -> &ThemeVariant;
fn theme(&self) -> &Arc<Theme>;
}
impl ActiveTheme for AppContext {
fn theme(&self) -> &ThemeVariant {
fn theme(&self) -> &Arc<Theme> {
&ThemeSettings::get_global(self).active_theme
}
}
pub struct ThemeFamily {
#[allow(dead_code)]
pub(crate) id: String,
pub id: String,
pub name: SharedString,
pub author: SharedString,
pub themes: Vec<ThemeVariant>,
pub themes: Vec<Theme>,
pub scales: ColorScales,
}
impl ThemeFamily {}
pub struct ThemeVariant {
#[allow(dead_code)]
pub(crate) id: String,
pub struct Theme {
pub id: String,
pub name: SharedString,
pub appearance: Appearance,
pub styles: ThemeStyles,
}
impl ThemeVariant {
impl Theme {
/// Returns the [`SystemColors`] for the theme.
#[inline(always)]
pub fn system(&self) -> &SystemColors {
&self.styles.system
}
/// Returns the [`ThemeColors`] for the theme.
#[inline(always)]
pub fn players(&self) -> &PlayerColors {
&self.styles.player
}
/// Returns the [`ThemeColors`] for the theme.
#[inline(always)]
pub fn colors(&self) -> &ThemeColors {
@ -66,7 +85,7 @@ impl ThemeVariant {
/// Returns the [`SyntaxTheme`] for the theme.
#[inline(always)]
pub fn syntax(&self) -> &SyntaxTheme {
pub fn syntax(&self) -> &Arc<SyntaxTheme> {
&self.styles.syntax
}
@ -76,15 +95,35 @@ impl ThemeVariant {
&self.styles.status
}
/// Returns the [`GitStatusColors`] for the theme.
#[inline(always)]
pub fn git(&self) -> &GitStatusColors {
&self.styles.git
}
/// Returns the color for the syntax node with the given name.
#[inline(always)]
pub fn syntax_color(&self, name: &str) -> Hsla {
self.syntax().color(name)
}
/// Returns the [`DiagnosticStyle`] for the theme.
#[inline(always)]
pub fn diagnostic_style(&self) -> DiagnosticStyle {
DiagnosticStyle {
error: self.status().error,
warning: self.status().warning,
info: self.status().info,
hint: self.status().info,
ignored: self.status().ignored,
}
}
}
#[derive(Clone, Debug)]
pub struct DiagnosticStyle {
pub error: Hsla,
pub warning: Hsla,
pub info: Hsla,
pub hint: Hsla,
pub ignored: Hsla,
}
#[cfg(feature = "stories")]
mod story;
#[cfg(feature = "stories")]
pub use story::*;

View file

@ -0,0 +1,85 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn andromeda() -> UserThemeFamily {
UserThemeFamily {
name: "Andromeda".into(),
author: "Eliver Lara (EliverLara)".into(),
themes: vec![
UserTheme {
name: "Andromeda".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x1b1d23ff).into()),
border_variant: Some(rgba(0x1b1d23ff).into()),
border_focused: Some(rgba(0x1b1d23ff).into()),
border_selected: Some(rgba(0x1b1d23ff).into()),
border_transparent: Some(rgba(0x1b1d23ff).into()),
border_disabled: Some(rgba(0x1b1d23ff).into()),
elevated_surface_background: Some(rgba(0x23262eff).into()),
surface_background: Some(rgba(0x23262eff).into()),
background: Some(rgba(0x23262eff).into()),
element_background: Some(rgba(0x00e8c5cc).into()),
text: Some(rgba(0xd4cdd8ff).into()),
tab_inactive_background: Some(rgba(0x23262eff).into()),
tab_active_background: Some(rgba(0x23262eff).into()),
terminal_ansi_bright_red: Some(rgba(0xee5d42ff).into()),
terminal_ansi_bright_green: Some(rgba(0x95e072ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffe66dff).into()),
terminal_ansi_bright_blue: Some(rgba(0x7bb7ffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xff00a9ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x00e8c6ff).into()),
terminal_ansi_red: Some(rgba(0xee5d42ff).into()),
terminal_ansi_green: Some(rgba(0x95e072ff).into()),
terminal_ansi_yellow: Some(rgba(0xffe66dff).into()),
terminal_ansi_blue: Some(rgba(0x7bb7ffff).into()),
terminal_ansi_magenta: Some(rgba(0xff00a9ff).into()),
terminal_ansi_cyan: Some(rgba(0x00e8c6ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Andromeda Bordered".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x1b1d23ff).into()),
border_variant: Some(rgba(0x1b1d23ff).into()),
border_focused: Some(rgba(0x1b1d23ff).into()),
border_selected: Some(rgba(0x1b1d23ff).into()),
border_transparent: Some(rgba(0x1b1d23ff).into()),
border_disabled: Some(rgba(0x1b1d23ff).into()),
elevated_surface_background: Some(rgba(0x23262eff).into()),
surface_background: Some(rgba(0x23262eff).into()),
background: Some(rgba(0x262933ff).into()),
element_background: Some(rgba(0x00e8c5cc).into()),
text: Some(rgba(0xd4cdd8ff).into()),
tab_inactive_background: Some(rgba(0x23262eff).into()),
tab_active_background: Some(rgba(0x262933ff).into()),
terminal_ansi_bright_red: Some(rgba(0xee5d42ff).into()),
terminal_ansi_bright_green: Some(rgba(0x95e072ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffe66dff).into()),
terminal_ansi_bright_blue: Some(rgba(0x7bb7ffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xff00a9ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x00e8c6ff).into()),
terminal_ansi_red: Some(rgba(0xee5d42ff).into()),
terminal_ansi_green: Some(rgba(0x95e072ff).into()),
terminal_ansi_yellow: Some(rgba(0xffe66dff).into()),
terminal_ansi_blue: Some(rgba(0x7bb7ffff).into()),
terminal_ansi_magenta: Some(rgba(0xff00a9ff).into()),
terminal_ansi_cyan: Some(rgba(0x00e8c6ff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,134 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn ayu() -> UserThemeFamily {
UserThemeFamily {
name: "Ayu".into(),
author: "dempfi (Ike Ku)".into(),
themes: vec![
UserTheme {
name: "Ayu Light".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x6b7d8f1f).into()),
border_variant: Some(rgba(0x6b7d8f1f).into()),
border_focused: Some(rgba(0x6b7d8f1f).into()),
border_selected: Some(rgba(0x6b7d8f1f).into()),
border_transparent: Some(rgba(0x6b7d8f1f).into()),
border_disabled: Some(rgba(0x6b7d8f1f).into()),
elevated_surface_background: Some(rgba(0xf8f9faff).into()),
surface_background: Some(rgba(0xf8f9faff).into()),
background: Some(rgba(0xf8f9faff).into()),
element_background: Some(rgba(0xffaa32ff).into()),
text: Some(rgba(0x8a9199ff).into()),
tab_inactive_background: Some(rgba(0xf8f9faff).into()),
tab_active_background: Some(rgba(0xf8f9faff).into()),
terminal_background: Some(rgba(0xf8f9faff).into()),
terminal_ansi_bright_black: Some(rgba(0x686868ff).into()),
terminal_ansi_bright_red: Some(rgba(0xef7070ff).into()),
terminal_ansi_bright_green: Some(rgba(0x86b300ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xf2ad48ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x389ee6ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xa37accff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x4bbf98ff).into()),
terminal_ansi_bright_white: Some(rgba(0xd1d1d1ff).into()),
terminal_ansi_black: Some(rgba(0x000000ff).into()),
terminal_ansi_red: Some(rgba(0xea6c6dff).into()),
terminal_ansi_green: Some(rgba(0x6cbf43ff).into()),
terminal_ansi_yellow: Some(rgba(0xeca944ff).into()),
terminal_ansi_blue: Some(rgba(0x3198e1ff).into()),
terminal_ansi_magenta: Some(rgba(0x9e75c7ff).into()),
terminal_ansi_cyan: Some(rgba(0x46ba94ff).into()),
terminal_ansi_white: Some(rgba(0xc7c7c7ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Ayu Mirage".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x171a24ff).into()),
border_variant: Some(rgba(0x171a24ff).into()),
border_focused: Some(rgba(0x171a24ff).into()),
border_selected: Some(rgba(0x171a24ff).into()),
border_transparent: Some(rgba(0x171a24ff).into()),
border_disabled: Some(rgba(0x171a24ff).into()),
elevated_surface_background: Some(rgba(0x1f2430ff).into()),
surface_background: Some(rgba(0x1f2430ff).into()),
background: Some(rgba(0x1f2430ff).into()),
element_background: Some(rgba(0xffcb65ff).into()),
text: Some(rgba(0x707a8cff).into()),
tab_inactive_background: Some(rgba(0x1f2430ff).into()),
tab_active_background: Some(rgba(0x1f2430ff).into()),
terminal_background: Some(rgba(0x1f2430ff).into()),
terminal_ansi_bright_black: Some(rgba(0x686868ff).into()),
terminal_ansi_bright_red: Some(rgba(0xf18678ff).into()),
terminal_ansi_bright_green: Some(rgba(0xd4fe7fff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffd173ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x73cfffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xdfbfffff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x95e6cbff).into()),
terminal_ansi_bright_white: Some(rgba(0xffffffff).into()),
terminal_ansi_black: Some(rgba(0x171a24ff).into()),
terminal_ansi_red: Some(rgba(0xed8173ff).into()),
terminal_ansi_green: Some(rgba(0x86d96bff).into()),
terminal_ansi_yellow: Some(rgba(0xfacc6eff).into()),
terminal_ansi_blue: Some(rgba(0x6ccafaff).into()),
terminal_ansi_magenta: Some(rgba(0xdabafaff).into()),
terminal_ansi_cyan: Some(rgba(0x90e1c6ff).into()),
terminal_ansi_white: Some(rgba(0xc7c7c7ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Ayu Dark".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x1e232bff).into()),
border_variant: Some(rgba(0x1e232bff).into()),
border_focused: Some(rgba(0x1e232bff).into()),
border_selected: Some(rgba(0x1e232bff).into()),
border_transparent: Some(rgba(0x1e232bff).into()),
border_disabled: Some(rgba(0x1e232bff).into()),
elevated_surface_background: Some(rgba(0x0b0e14ff).into()),
surface_background: Some(rgba(0x0b0e14ff).into()),
background: Some(rgba(0x0b0e14ff).into()),
element_background: Some(rgba(0xe6b450ff).into()),
text: Some(rgba(0x565b66ff).into()),
tab_inactive_background: Some(rgba(0x0b0e14ff).into()),
tab_active_background: Some(rgba(0x0b0e14ff).into()),
terminal_background: Some(rgba(0x0b0e14ff).into()),
terminal_ansi_bright_black: Some(rgba(0x686868ff).into()),
terminal_ansi_bright_red: Some(rgba(0xef7077ff).into()),
terminal_ansi_bright_green: Some(rgba(0xa9d94bff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffb353ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x59c2ffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xd2a6ffff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x95e6cbff).into()),
terminal_ansi_bright_white: Some(rgba(0xffffffff).into()),
terminal_ansi_black: Some(rgba(0x1e232bff).into()),
terminal_ansi_red: Some(rgba(0xea6c72ff).into()),
terminal_ansi_green: Some(rgba(0x7ed962ff).into()),
terminal_ansi_yellow: Some(rgba(0xf9af4fff).into()),
terminal_ansi_blue: Some(rgba(0x52bdfaff).into()),
terminal_ansi_magenta: Some(rgba(0xcca1faff).into()),
terminal_ansi_cyan: Some(rgba(0x90e1c6ff).into()),
terminal_ansi_white: Some(rgba(0xc7c7c7ff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,54 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn dracula() -> UserThemeFamily {
UserThemeFamily {
name: "Dracula".into(),
author: "Zeno Rocha".into(),
themes: vec![UserTheme {
name: "Dracula".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xbd93f9ff).into()),
border_variant: Some(rgba(0xbd93f9ff).into()),
border_focused: Some(rgba(0xbd93f9ff).into()),
border_selected: Some(rgba(0xbd93f9ff).into()),
border_transparent: Some(rgba(0xbd93f9ff).into()),
border_disabled: Some(rgba(0xbd93f9ff).into()),
elevated_surface_background: Some(rgba(0x282a35ff).into()),
surface_background: Some(rgba(0x282a35ff).into()),
background: Some(rgba(0x282a35ff).into()),
element_background: Some(rgba(0x44475aff).into()),
text: Some(rgba(0xf8f8f2ff).into()),
tab_inactive_background: Some(rgba(0x21222cff).into()),
tab_active_background: Some(rgba(0x282a35ff).into()),
terminal_background: Some(rgba(0x282a35ff).into()),
terminal_ansi_bright_black: Some(rgba(0x6272a4ff).into()),
terminal_ansi_bright_red: Some(rgba(0xff6d6dff).into()),
terminal_ansi_bright_green: Some(rgba(0x69ff94ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffffa5ff).into()),
terminal_ansi_bright_blue: Some(rgba(0xd6abfeff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xff92dfff).into()),
terminal_ansi_bright_cyan: Some(rgba(0xa3fefeff).into()),
terminal_ansi_bright_white: Some(rgba(0xffffffff).into()),
terminal_ansi_black: Some(rgba(0x21222cff).into()),
terminal_ansi_red: Some(rgba(0xff5555ff).into()),
terminal_ansi_green: Some(rgba(0x50fa7bff).into()),
terminal_ansi_yellow: Some(rgba(0xf1fa8cff).into()),
terminal_ansi_blue: Some(rgba(0xbd93f9ff).into()),
terminal_ansi_magenta: Some(rgba(0xff79c6ff).into()),
terminal_ansi_cyan: Some(rgba(0x8be9fdff).into()),
terminal_ansi_white: Some(rgba(0xf8f8f2ff).into()),
..Default::default()
},
},
}],
}
}

View file

@ -0,0 +1,239 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn gruvbox() -> UserThemeFamily {
UserThemeFamily {
name: "Gruvbox".into(),
author: "morhetz".into(),
themes: vec![
UserTheme {
name: "Gruvbox Dark Hard".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x3c3836ff).into()),
border_variant: Some(rgba(0x3c3836ff).into()),
border_focused: Some(rgba(0x3c3836ff).into()),
border_selected: Some(rgba(0x3c3836ff).into()),
border_transparent: Some(rgba(0x3c3836ff).into()),
border_disabled: Some(rgba(0x3c3836ff).into()),
background: Some(rgba(0x1d2021ff).into()),
element_background: Some(rgba(0x44858780).into()),
text: Some(rgba(0xebdbb2ff).into()),
tab_inactive_background: Some(rgba(0x1d2021ff).into()),
tab_active_background: Some(rgba(0x32302fff).into()),
terminal_background: Some(rgba(0x1d2021ff).into()),
terminal_ansi_bright_black: Some(rgba(0x928374ff).into()),
terminal_ansi_bright_red: Some(rgba(0xfb4833ff).into()),
terminal_ansi_bright_green: Some(rgba(0xb8bb25ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xfabd2eff).into()),
terminal_ansi_bright_blue: Some(rgba(0x83a598ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xd3869bff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x8ec07cff).into()),
terminal_ansi_bright_white: Some(rgba(0xebdbb2ff).into()),
terminal_ansi_black: Some(rgba(0x3c3836ff).into()),
terminal_ansi_red: Some(rgba(0xcc241cff).into()),
terminal_ansi_green: Some(rgba(0x989719ff).into()),
terminal_ansi_yellow: Some(rgba(0xd79920ff).into()),
terminal_ansi_blue: Some(rgba(0x448587ff).into()),
terminal_ansi_magenta: Some(rgba(0xb16185ff).into()),
terminal_ansi_cyan: Some(rgba(0x679d6aff).into()),
terminal_ansi_white: Some(rgba(0xa89984ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Gruvbox Dark Medium".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x3c3836ff).into()),
border_variant: Some(rgba(0x3c3836ff).into()),
border_focused: Some(rgba(0x3c3836ff).into()),
border_selected: Some(rgba(0x3c3836ff).into()),
border_transparent: Some(rgba(0x3c3836ff).into()),
border_disabled: Some(rgba(0x3c3836ff).into()),
background: Some(rgba(0x282828ff).into()),
element_background: Some(rgba(0x44858780).into()),
text: Some(rgba(0xebdbb2ff).into()),
tab_inactive_background: Some(rgba(0x282828ff).into()),
tab_active_background: Some(rgba(0x3c3836ff).into()),
terminal_background: Some(rgba(0x282828ff).into()),
terminal_ansi_bright_black: Some(rgba(0x928374ff).into()),
terminal_ansi_bright_red: Some(rgba(0xfb4833ff).into()),
terminal_ansi_bright_green: Some(rgba(0xb8bb25ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xfabd2eff).into()),
terminal_ansi_bright_blue: Some(rgba(0x83a598ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xd3869bff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x8ec07cff).into()),
terminal_ansi_bright_white: Some(rgba(0xebdbb2ff).into()),
terminal_ansi_black: Some(rgba(0x3c3836ff).into()),
terminal_ansi_red: Some(rgba(0xcc241cff).into()),
terminal_ansi_green: Some(rgba(0x989719ff).into()),
terminal_ansi_yellow: Some(rgba(0xd79920ff).into()),
terminal_ansi_blue: Some(rgba(0x448587ff).into()),
terminal_ansi_magenta: Some(rgba(0xb16185ff).into()),
terminal_ansi_cyan: Some(rgba(0x679d6aff).into()),
terminal_ansi_white: Some(rgba(0xa89984ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Gruvbox Dark Soft".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x3c3836ff).into()),
border_variant: Some(rgba(0x3c3836ff).into()),
border_focused: Some(rgba(0x3c3836ff).into()),
border_selected: Some(rgba(0x3c3836ff).into()),
border_transparent: Some(rgba(0x3c3836ff).into()),
border_disabled: Some(rgba(0x3c3836ff).into()),
background: Some(rgba(0x32302fff).into()),
element_background: Some(rgba(0x44858780).into()),
text: Some(rgba(0xebdbb2ff).into()),
tab_inactive_background: Some(rgba(0x32302fff).into()),
tab_active_background: Some(rgba(0x504945ff).into()),
terminal_background: Some(rgba(0x32302fff).into()),
terminal_ansi_bright_black: Some(rgba(0x928374ff).into()),
terminal_ansi_bright_red: Some(rgba(0xfb4833ff).into()),
terminal_ansi_bright_green: Some(rgba(0xb8bb25ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xfabd2eff).into()),
terminal_ansi_bright_blue: Some(rgba(0x83a598ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xd3869bff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x8ec07cff).into()),
terminal_ansi_bright_white: Some(rgba(0xebdbb2ff).into()),
terminal_ansi_black: Some(rgba(0x3c3836ff).into()),
terminal_ansi_red: Some(rgba(0xcc241cff).into()),
terminal_ansi_green: Some(rgba(0x989719ff).into()),
terminal_ansi_yellow: Some(rgba(0xd79920ff).into()),
terminal_ansi_blue: Some(rgba(0x448587ff).into()),
terminal_ansi_magenta: Some(rgba(0xb16185ff).into()),
terminal_ansi_cyan: Some(rgba(0x679d6aff).into()),
terminal_ansi_white: Some(rgba(0xa89984ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Gruvbox Light Hard".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xebdbb2ff).into()),
border_variant: Some(rgba(0xebdbb2ff).into()),
border_focused: Some(rgba(0xebdbb2ff).into()),
border_selected: Some(rgba(0xebdbb2ff).into()),
border_transparent: Some(rgba(0xebdbb2ff).into()),
border_disabled: Some(rgba(0xebdbb2ff).into()),
background: Some(rgba(0xf9f5d7ff).into()),
element_background: Some(rgba(0x44858780).into()),
text: Some(rgba(0x3c3836ff).into()),
tab_inactive_background: Some(rgba(0xf9f5d7ff).into()),
tab_active_background: Some(rgba(0xf2e5bcff).into()),
terminal_background: Some(rgba(0xf9f5d7ff).into()),
terminal_ansi_bright_black: Some(rgba(0x928374ff).into()),
terminal_ansi_bright_red: Some(rgba(0x9d0006ff).into()),
terminal_ansi_bright_green: Some(rgba(0x79740eff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xb57613ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x066578ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x8f3e71ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x427b58ff).into()),
terminal_ansi_bright_white: Some(rgba(0x3c3836ff).into()),
terminal_ansi_black: Some(rgba(0xebdbb2ff).into()),
terminal_ansi_red: Some(rgba(0xcc241cff).into()),
terminal_ansi_green: Some(rgba(0x989719ff).into()),
terminal_ansi_yellow: Some(rgba(0xd79920ff).into()),
terminal_ansi_blue: Some(rgba(0x448587ff).into()),
terminal_ansi_magenta: Some(rgba(0xb16185ff).into()),
terminal_ansi_cyan: Some(rgba(0x679d6aff).into()),
terminal_ansi_white: Some(rgba(0x7c6f64ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Gruvbox Light Medium".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xebdbb2ff).into()),
border_variant: Some(rgba(0xebdbb2ff).into()),
border_focused: Some(rgba(0xebdbb2ff).into()),
border_selected: Some(rgba(0xebdbb2ff).into()),
border_transparent: Some(rgba(0xebdbb2ff).into()),
border_disabled: Some(rgba(0xebdbb2ff).into()),
background: Some(rgba(0xfbf1c7ff).into()),
element_background: Some(rgba(0x44858780).into()),
text: Some(rgba(0x3c3836ff).into()),
tab_inactive_background: Some(rgba(0xfbf1c7ff).into()),
tab_active_background: Some(rgba(0xebdbb2ff).into()),
terminal_background: Some(rgba(0xfbf1c7ff).into()),
terminal_ansi_bright_black: Some(rgba(0x928374ff).into()),
terminal_ansi_bright_red: Some(rgba(0x9d0006ff).into()),
terminal_ansi_bright_green: Some(rgba(0x79740eff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xb57613ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x066578ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x8f3e71ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x427b58ff).into()),
terminal_ansi_bright_white: Some(rgba(0x3c3836ff).into()),
terminal_ansi_black: Some(rgba(0xebdbb2ff).into()),
terminal_ansi_red: Some(rgba(0xcc241cff).into()),
terminal_ansi_green: Some(rgba(0x989719ff).into()),
terminal_ansi_yellow: Some(rgba(0xd79920ff).into()),
terminal_ansi_blue: Some(rgba(0x448587ff).into()),
terminal_ansi_magenta: Some(rgba(0xb16185ff).into()),
terminal_ansi_cyan: Some(rgba(0x679d6aff).into()),
terminal_ansi_white: Some(rgba(0x7c6f64ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Gruvbox Light Soft".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xebdbb2ff).into()),
border_variant: Some(rgba(0xebdbb2ff).into()),
border_focused: Some(rgba(0xebdbb2ff).into()),
border_selected: Some(rgba(0xebdbb2ff).into()),
border_transparent: Some(rgba(0xebdbb2ff).into()),
border_disabled: Some(rgba(0xebdbb2ff).into()),
background: Some(rgba(0xf2e5bcff).into()),
element_background: Some(rgba(0x44858780).into()),
text: Some(rgba(0x3c3836ff).into()),
tab_inactive_background: Some(rgba(0xf2e5bcff).into()),
tab_active_background: Some(rgba(0xd5c4a1ff).into()),
terminal_background: Some(rgba(0xf2e5bcff).into()),
terminal_ansi_bright_black: Some(rgba(0x928374ff).into()),
terminal_ansi_bright_red: Some(rgba(0x9d0006ff).into()),
terminal_ansi_bright_green: Some(rgba(0x79740eff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xb57613ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x066578ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x8f3e71ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x427b58ff).into()),
terminal_ansi_bright_white: Some(rgba(0x3c3836ff).into()),
terminal_ansi_black: Some(rgba(0xebdbb2ff).into()),
terminal_ansi_red: Some(rgba(0xcc241cff).into()),
terminal_ansi_green: Some(rgba(0x989719ff).into()),
terminal_ansi_yellow: Some(rgba(0xd79920ff).into()),
terminal_ansi_blue: Some(rgba(0x448587ff).into()),
terminal_ansi_magenta: Some(rgba(0xb16185ff).into()),
terminal_ansi_cyan: Some(rgba(0x679d6aff).into()),
terminal_ansi_white: Some(rgba(0x7c6f64ff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,44 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
mod andromeda;
mod ayu;
mod dracula;
mod gruvbox;
mod night_owl;
mod nord;
mod notctis;
mod palenight;
mod rose_pine;
mod solarized;
mod synthwave_84;
pub use andromeda::*;
pub use ayu::*;
pub use dracula::*;
pub use gruvbox::*;
pub use night_owl::*;
pub use nord::*;
pub use notctis::*;
pub use palenight::*;
pub use rose_pine::*;
pub use solarized::*;
pub use synthwave_84::*;
use crate::UserThemeFamily;
pub(crate) fn all_user_themes() -> Vec<UserThemeFamily> {
vec![
rose_pine(),
night_owl(),
andromeda(),
synthwave_84(),
palenight(),
dracula(),
solarized(),
nord(),
notctis(),
ayu(),
gruvbox(),
]
}

View file

@ -0,0 +1,94 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn night_owl() -> UserThemeFamily {
UserThemeFamily {
name: "Night Owl".into(),
author: "Sarah Drasner (sdras)".into(),
themes: vec![
UserTheme {
name: "Night Owl".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x5f7e97ff).into()),
border_variant: Some(rgba(0x5f7e97ff).into()),
border_focused: Some(rgba(0x5f7e97ff).into()),
border_selected: Some(rgba(0x5f7e97ff).into()),
border_transparent: Some(rgba(0x5f7e97ff).into()),
border_disabled: Some(rgba(0x5f7e97ff).into()),
elevated_surface_background: Some(rgba(0x011526ff).into()),
surface_background: Some(rgba(0x011526ff).into()),
background: Some(rgba(0x011526ff).into()),
element_background: Some(rgba(0x7d56c1cc).into()),
text: Some(rgba(0xd6deebff).into()),
tab_inactive_background: Some(rgba(0x01101cff).into()),
tab_active_background: Some(rgba(0x0a2842ff).into()),
terminal_ansi_bright_black: Some(rgba(0x575656ff).into()),
terminal_ansi_bright_red: Some(rgba(0xef524fff).into()),
terminal_ansi_bright_green: Some(rgba(0x21da6eff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffeb95ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x7fdbcaff).into()),
terminal_ansi_bright_white: Some(rgba(0xffffffff).into()),
terminal_ansi_black: Some(rgba(0x011526ff).into()),
terminal_ansi_red: Some(rgba(0xef524fff).into()),
terminal_ansi_green: Some(rgba(0x21da6eff).into()),
terminal_ansi_yellow: Some(rgba(0xc5e478ff).into()),
terminal_ansi_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_cyan: Some(rgba(0x20c7a7ff).into()),
terminal_ansi_white: Some(rgba(0xffffffff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Night Owl Light".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xd9d9d9ff).into()),
border_variant: Some(rgba(0xd9d9d9ff).into()),
border_focused: Some(rgba(0xd9d9d9ff).into()),
border_selected: Some(rgba(0xd9d9d9ff).into()),
border_transparent: Some(rgba(0xd9d9d9ff).into()),
border_disabled: Some(rgba(0xd9d9d9ff).into()),
elevated_surface_background: Some(rgba(0xf0f0f0ff).into()),
surface_background: Some(rgba(0xf0f0f0ff).into()),
background: Some(rgba(0xfbfbfbff).into()),
element_background: Some(rgba(0x29a298ff).into()),
text: Some(rgba(0x403f53ff).into()),
tab_inactive_background: Some(rgba(0xf0f0f0ff).into()),
tab_active_background: Some(rgba(0xf6f6f6ff).into()),
terminal_background: Some(rgba(0xf6f6f6ff).into()),
terminal_ansi_bright_black: Some(rgba(0x403f53ff).into()),
terminal_ansi_bright_red: Some(rgba(0xde3c3aff).into()),
terminal_ansi_bright_green: Some(rgba(0x07916aff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xdaa900ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x278dd7ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xd64289ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x29a298ff).into()),
terminal_ansi_bright_white: Some(rgba(0xf0f0f0ff).into()),
terminal_ansi_black: Some(rgba(0x403f53ff).into()),
terminal_ansi_red: Some(rgba(0xde3c3aff).into()),
terminal_ansi_green: Some(rgba(0x07916aff).into()),
terminal_ansi_yellow: Some(rgba(0xe0ae01ff).into()),
terminal_ansi_blue: Some(rgba(0x278dd7ff).into()),
terminal_ansi_magenta: Some(rgba(0xd64289ff).into()),
terminal_ansi_cyan: Some(rgba(0x29a298ff).into()),
terminal_ansi_white: Some(rgba(0xf0f0f0ff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,54 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn nord() -> UserThemeFamily {
UserThemeFamily {
name: "Nord".into(),
author: "Sven Greb (svengreb)".into(),
themes: vec![UserTheme {
name: "Nord".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x3b4252ff).into()),
border_variant: Some(rgba(0x3b4252ff).into()),
border_focused: Some(rgba(0x3b4252ff).into()),
border_selected: Some(rgba(0x3b4252ff).into()),
border_transparent: Some(rgba(0x3b4252ff).into()),
border_disabled: Some(rgba(0x3b4252ff).into()),
elevated_surface_background: Some(rgba(0x2e3440ff).into()),
surface_background: Some(rgba(0x2e3440ff).into()),
background: Some(rgba(0x2e3440ff).into()),
element_background: Some(rgba(0x88bfd0ee).into()),
text: Some(rgba(0xd8dee9ff).into()),
tab_inactive_background: Some(rgba(0x2e3440ff).into()),
tab_active_background: Some(rgba(0x3b4252ff).into()),
terminal_background: Some(rgba(0x2e3440ff).into()),
terminal_ansi_bright_black: Some(rgba(0x4c566aff).into()),
terminal_ansi_bright_red: Some(rgba(0xbf616aff).into()),
terminal_ansi_bright_green: Some(rgba(0xa3be8cff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xebcb8bff).into()),
terminal_ansi_bright_blue: Some(rgba(0x81a1c1ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xb48eacff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x8fbcbbff).into()),
terminal_ansi_bright_white: Some(rgba(0xeceff4ff).into()),
terminal_ansi_black: Some(rgba(0x3b4252ff).into()),
terminal_ansi_red: Some(rgba(0xbf616aff).into()),
terminal_ansi_green: Some(rgba(0xa3be8cff).into()),
terminal_ansi_yellow: Some(rgba(0xebcb8bff).into()),
terminal_ansi_blue: Some(rgba(0x81a1c1ff).into()),
terminal_ansi_magenta: Some(rgba(0xb48eacff).into()),
terminal_ansi_cyan: Some(rgba(0x88bfd0ff).into()),
terminal_ansi_white: Some(rgba(0xe5e9f0ff).into()),
..Default::default()
},
},
}],
}
}

View file

@ -0,0 +1,446 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn notctis() -> UserThemeFamily {
UserThemeFamily {
name: "Notctis".into(),
author: "Liviu Schera (liviuschera)".into(),
themes: vec![
UserTheme {
name: "Noctis Azureus".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x1579b6ff).into()),
border_variant: Some(rgba(0x1579b6ff).into()),
border_focused: Some(rgba(0x1579b6ff).into()),
border_selected: Some(rgba(0x1579b6ff).into()),
border_transparent: Some(rgba(0x1579b6ff).into()),
border_disabled: Some(rgba(0x1579b6ff).into()),
elevated_surface_background: Some(rgba(0x051b28ff).into()),
surface_background: Some(rgba(0x051b28ff).into()),
background: Some(rgba(0x07263aff).into()),
element_background: Some(rgba(0x007e99ff).into()),
text: Some(rgba(0xbecfdaff).into()),
tab_inactive_background: Some(rgba(0x08324eff).into()),
tab_active_background: Some(rgba(0x07263aff).into()),
terminal_background: Some(rgba(0x051b28ff).into()),
terminal_ansi_bright_black: Some(rgba(0x475e6cff).into()),
terminal_ansi_bright_red: Some(rgba(0xe97749ff).into()),
terminal_ansi_bright_green: Some(rgba(0x5febb1ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe69532ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x5fb5ebff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xe697b2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x5fdaebff).into()),
terminal_ansi_bright_white: Some(rgba(0xbecfdaff).into()),
terminal_ansi_black: Some(rgba(0x28343dff).into()),
terminal_ansi_red: Some(rgba(0xe66432ff).into()),
terminal_ansi_green: Some(rgba(0x49e9a6ff).into()),
terminal_ansi_yellow: Some(rgba(0xe4b781ff).into()),
terminal_ansi_blue: Some(rgba(0x49ace9ff).into()),
terminal_ansi_magenta: Some(rgba(0xdf759aff).into()),
terminal_ansi_cyan: Some(rgba(0x49d5e9ff).into()),
terminal_ansi_white: Some(rgba(0xaec3d0ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Bordo".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x997582ff).into()),
border_variant: Some(rgba(0x997582ff).into()),
border_focused: Some(rgba(0x997582ff).into()),
border_selected: Some(rgba(0x997582ff).into()),
border_transparent: Some(rgba(0x997582ff).into()),
border_disabled: Some(rgba(0x997582ff).into()),
elevated_surface_background: Some(rgba(0x272022ff).into()),
surface_background: Some(rgba(0x272022ff).into()),
background: Some(rgba(0x322a2dff).into()),
element_background: Some(rgba(0x007e99ff).into()),
text: Some(rgba(0xcbbec2ff).into()),
tab_inactive_background: Some(rgba(0x413036ff).into()),
tab_active_background: Some(rgba(0x322a2dff).into()),
terminal_background: Some(rgba(0x272022ff).into()),
terminal_ansi_bright_black: Some(rgba(0x69545bff).into()),
terminal_ansi_bright_red: Some(rgba(0xe97749ff).into()),
terminal_ansi_bright_green: Some(rgba(0x5febb1ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe69532ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x5fb5ebff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xe697b2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x5fdaebff).into()),
terminal_ansi_bright_white: Some(rgba(0xcbbec2ff).into()),
terminal_ansi_black: Some(rgba(0x47393eff).into()),
terminal_ansi_red: Some(rgba(0xe66432ff).into()),
terminal_ansi_green: Some(rgba(0x49e9a6ff).into()),
terminal_ansi_yellow: Some(rgba(0xe4b781ff).into()),
terminal_ansi_blue: Some(rgba(0x49ace9ff).into()),
terminal_ansi_magenta: Some(rgba(0xdf759aff).into()),
terminal_ansi_cyan: Some(rgba(0x49d5e9ff).into()),
terminal_ansi_white: Some(rgba(0xb9acb0ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctus Hibernus".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x00c6e0ff).into()),
border_variant: Some(rgba(0x00c6e0ff).into()),
border_focused: Some(rgba(0x00c6e0ff).into()),
border_selected: Some(rgba(0x00c6e0ff).into()),
border_transparent: Some(rgba(0x00c6e0ff).into()),
border_disabled: Some(rgba(0x00c6e0ff).into()),
elevated_surface_background: Some(rgba(0xe1eeefff).into()),
surface_background: Some(rgba(0xe1eeefff).into()),
background: Some(rgba(0xf4f6f6ff).into()),
element_background: Some(rgba(0x089099ff).into()),
text: Some(rgba(0x005661ff).into()),
tab_inactive_background: Some(rgba(0xcaedf2ff).into()),
tab_active_background: Some(rgba(0xf4f6f6ff).into()),
terminal_background: Some(rgba(0xe1eeefff).into()),
terminal_ansi_bright_black: Some(rgba(0x004d57ff).into()),
terminal_ansi_bright_red: Some(rgba(0xff3f00ff).into()),
terminal_ansi_bright_green: Some(rgba(0x00d17aff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xff8c00ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x0ea3ffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xff6b9eff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x00cae6ff).into()),
terminal_ansi_bright_white: Some(rgba(0xbbc3c4ff).into()),
terminal_ansi_black: Some(rgba(0x003b41ff).into()),
terminal_ansi_red: Some(rgba(0xe34d1bff).into()),
terminal_ansi_green: Some(rgba(0x00b368ff).into()),
terminal_ansi_yellow: Some(rgba(0xf49724ff).into()),
terminal_ansi_blue: Some(rgba(0x0094f0ff).into()),
terminal_ansi_magenta: Some(rgba(0xff5792ff).into()),
terminal_ansi_cyan: Some(rgba(0x00bdd6ff).into()),
terminal_ansi_white: Some(rgba(0x8ca6a6ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Lilac".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xaea4f4ff).into()),
border_variant: Some(rgba(0xaea4f4ff).into()),
border_focused: Some(rgba(0xaea4f4ff).into()),
border_selected: Some(rgba(0xaea4f4ff).into()),
border_transparent: Some(rgba(0xaea4f4ff).into()),
border_disabled: Some(rgba(0xaea4f4ff).into()),
elevated_surface_background: Some(rgba(0xe9e7f3ff).into()),
surface_background: Some(rgba(0xe9e7f3ff).into()),
background: Some(rgba(0xf2f1f8ff).into()),
element_background: Some(rgba(0x8d7ffeff).into()),
text: Some(rgba(0x0c006bff).into()),
tab_inactive_background: Some(rgba(0xe2dff6ff).into()),
tab_active_background: Some(rgba(0xf2f1f8ff).into()),
terminal_background: Some(rgba(0xe9e7f3ff).into()),
terminal_ansi_bright_black: Some(rgba(0x0f0080ff).into()),
terminal_ansi_bright_red: Some(rgba(0xff3f00ff).into()),
terminal_ansi_bright_green: Some(rgba(0x00d17aff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xff8c00ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x0ea3ffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xff6b9eff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x00cae6ff).into()),
terminal_ansi_bright_white: Some(rgba(0xbbc3c4ff).into()),
terminal_ansi_black: Some(rgba(0x0c006bff).into()),
terminal_ansi_red: Some(rgba(0xe34d1bff).into()),
terminal_ansi_green: Some(rgba(0x00b368ff).into()),
terminal_ansi_yellow: Some(rgba(0xf49724ff).into()),
terminal_ansi_blue: Some(rgba(0x0094f0ff).into()),
terminal_ansi_magenta: Some(rgba(0xff5792ff).into()),
terminal_ansi_cyan: Some(rgba(0x00bdd6ff).into()),
terminal_ansi_white: Some(rgba(0x8ca6a6ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Lux".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x00c6e0ff).into()),
border_variant: Some(rgba(0x00c6e0ff).into()),
border_focused: Some(rgba(0x00c6e0ff).into()),
border_selected: Some(rgba(0x00c6e0ff).into()),
border_transparent: Some(rgba(0x00c6e0ff).into()),
border_disabled: Some(rgba(0x00c6e0ff).into()),
elevated_surface_background: Some(rgba(0xf6eddaff).into()),
surface_background: Some(rgba(0xf6eddaff).into()),
background: Some(rgba(0xfef8ecff).into()),
element_background: Some(rgba(0x089099ff).into()),
text: Some(rgba(0x005661ff).into()),
tab_inactive_background: Some(rgba(0xf0e9d6ff).into()),
tab_active_background: Some(rgba(0xfef8ecff).into()),
terminal_background: Some(rgba(0xf6eddaff).into()),
terminal_ansi_bright_black: Some(rgba(0x004d57ff).into()),
terminal_ansi_bright_red: Some(rgba(0xff3f00ff).into()),
terminal_ansi_bright_green: Some(rgba(0x00d17aff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xff8c00ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x0ea3ffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xff6b9eff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x00cae6ff).into()),
terminal_ansi_bright_white: Some(rgba(0xbbc3c4ff).into()),
terminal_ansi_black: Some(rgba(0x003b41ff).into()),
terminal_ansi_red: Some(rgba(0xe34d1bff).into()),
terminal_ansi_green: Some(rgba(0x00b368ff).into()),
terminal_ansi_yellow: Some(rgba(0xf49724ff).into()),
terminal_ansi_blue: Some(rgba(0x0094f0ff).into()),
terminal_ansi_magenta: Some(rgba(0xff5792ff).into()),
terminal_ansi_cyan: Some(rgba(0x00bdd6ff).into()),
terminal_ansi_white: Some(rgba(0x8ca6a6ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Minimus".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x496c83ff).into()),
border_variant: Some(rgba(0x496c83ff).into()),
border_focused: Some(rgba(0x496c83ff).into()),
border_selected: Some(rgba(0x496c83ff).into()),
border_transparent: Some(rgba(0x496c83ff).into()),
border_disabled: Some(rgba(0x496c83ff).into()),
elevated_surface_background: Some(rgba(0x0e1920ff).into()),
surface_background: Some(rgba(0x0e1920ff).into()),
background: Some(rgba(0x1b2932ff).into()),
element_background: Some(rgba(0x2e616bff).into()),
text: Some(rgba(0xc5cdd3ff).into()),
tab_inactive_background: Some(rgba(0x202d37ff).into()),
tab_active_background: Some(rgba(0x1b2932ff).into()),
terminal_background: Some(rgba(0x0e1920ff).into()),
terminal_ansi_bright_black: Some(rgba(0x425866ff).into()),
terminal_ansi_bright_red: Some(rgba(0xca8468ff).into()),
terminal_ansi_bright_green: Some(rgba(0x84c8abff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xd1aa7bff).into()),
terminal_ansi_bright_blue: Some(rgba(0x68a4caff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xc88da2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x84bfc8ff).into()),
terminal_ansi_bright_white: Some(rgba(0xc5d1d3ff).into()),
terminal_ansi_black: Some(rgba(0x182935ff).into()),
terminal_ansi_red: Some(rgba(0xc08872ff).into()),
terminal_ansi_green: Some(rgba(0x72c09fff).into()),
terminal_ansi_yellow: Some(rgba(0xc8a984ff).into()),
terminal_ansi_blue: Some(rgba(0x6095b7ff).into()),
terminal_ansi_magenta: Some(rgba(0xc28097ff).into()),
terminal_ansi_cyan: Some(rgba(0x72b7c0ff).into()),
terminal_ansi_white: Some(rgba(0xc5cdd3ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x0d6571ff).into()),
border_variant: Some(rgba(0x0d6571ff).into()),
border_focused: Some(rgba(0x0d6571ff).into()),
border_selected: Some(rgba(0x0d6571ff).into()),
border_transparent: Some(rgba(0x0d6571ff).into()),
border_disabled: Some(rgba(0x0d6571ff).into()),
elevated_surface_background: Some(rgba(0x03181aff).into()),
surface_background: Some(rgba(0x03181aff).into()),
background: Some(rgba(0x052428ff).into()),
element_background: Some(rgba(0x089099ff).into()),
text: Some(rgba(0xb1c9ccff).into()),
tab_inactive_background: Some(rgba(0x052e32ff).into()),
tab_active_background: Some(rgba(0x052428ff).into()),
terminal_background: Some(rgba(0x03181aff).into()),
terminal_ansi_bright_black: Some(rgba(0x47686cff).into()),
terminal_ansi_bright_red: Some(rgba(0xe97749ff).into()),
terminal_ansi_bright_green: Some(rgba(0x5febb1ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe69532ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x5fb5ebff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xe697b2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x5fdaebff).into()),
terminal_ansi_bright_white: Some(rgba(0xc1d4d7ff).into()),
terminal_ansi_black: Some(rgba(0x324a4dff).into()),
terminal_ansi_red: Some(rgba(0xe66432ff).into()),
terminal_ansi_green: Some(rgba(0x49e9a6ff).into()),
terminal_ansi_yellow: Some(rgba(0xe4b781ff).into()),
terminal_ansi_blue: Some(rgba(0x49ace9ff).into()),
terminal_ansi_magenta: Some(rgba(0xdf759aff).into()),
terminal_ansi_cyan: Some(rgba(0x49d5e9ff).into()),
terminal_ansi_white: Some(rgba(0xb1c9ccff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Obscuro".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x0d6571ff).into()),
border_variant: Some(rgba(0x0d6571ff).into()),
border_focused: Some(rgba(0x0d6571ff).into()),
border_selected: Some(rgba(0x0d6571ff).into()),
border_transparent: Some(rgba(0x0d6571ff).into()),
border_disabled: Some(rgba(0x0d6571ff).into()),
elevated_surface_background: Some(rgba(0x020c0eff).into()),
surface_background: Some(rgba(0x020c0eff).into()),
background: Some(rgba(0x031316ff).into()),
element_background: Some(rgba(0x089099ff).into()),
text: Some(rgba(0xb1c9ccff).into()),
tab_inactive_background: Some(rgba(0x052e32ff).into()),
tab_active_background: Some(rgba(0x031316ff).into()),
terminal_background: Some(rgba(0x020c0eff).into()),
terminal_ansi_bright_black: Some(rgba(0x47686cff).into()),
terminal_ansi_bright_red: Some(rgba(0xe97749ff).into()),
terminal_ansi_bright_green: Some(rgba(0x5febb1ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe69532ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x5fb5ebff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xe697b2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x5fdaebff).into()),
terminal_ansi_bright_white: Some(rgba(0xc1d4d7ff).into()),
terminal_ansi_black: Some(rgba(0x324a4dff).into()),
terminal_ansi_red: Some(rgba(0xe66432ff).into()),
terminal_ansi_green: Some(rgba(0x49e9a6ff).into()),
terminal_ansi_yellow: Some(rgba(0xe4b781ff).into()),
terminal_ansi_blue: Some(rgba(0x49ace9ff).into()),
terminal_ansi_magenta: Some(rgba(0xdf759aff).into()),
terminal_ansi_cyan: Some(rgba(0x49d5e9ff).into()),
terminal_ansi_white: Some(rgba(0xb1c9ccff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Sereno".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x0d6571ff).into()),
border_variant: Some(rgba(0x0d6571ff).into()),
border_focused: Some(rgba(0x0d6571ff).into()),
border_selected: Some(rgba(0x0d6571ff).into()),
border_transparent: Some(rgba(0x0d6571ff).into()),
border_disabled: Some(rgba(0x0d6571ff).into()),
elevated_surface_background: Some(rgba(0x020c0eff).into()),
surface_background: Some(rgba(0x020c0eff).into()),
background: Some(rgba(0x031316ff).into()),
element_background: Some(rgba(0x089099ff).into()),
text: Some(rgba(0xb1c9ccff).into()),
tab_inactive_background: Some(rgba(0x052e32ff).into()),
tab_active_background: Some(rgba(0x031316ff).into()),
terminal_background: Some(rgba(0x020c0eff).into()),
terminal_ansi_bright_black: Some(rgba(0x47686cff).into()),
terminal_ansi_bright_red: Some(rgba(0xe97749ff).into()),
terminal_ansi_bright_green: Some(rgba(0x5febb1ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe69532ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x5fb5ebff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xe697b2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x5fdaebff).into()),
terminal_ansi_bright_white: Some(rgba(0xc1d4d7ff).into()),
terminal_ansi_black: Some(rgba(0x324a4dff).into()),
terminal_ansi_red: Some(rgba(0xe66432ff).into()),
terminal_ansi_green: Some(rgba(0x49e9a6ff).into()),
terminal_ansi_yellow: Some(rgba(0xe4b781ff).into()),
terminal_ansi_blue: Some(rgba(0x49ace9ff).into()),
terminal_ansi_magenta: Some(rgba(0xdf759aff).into()),
terminal_ansi_cyan: Some(rgba(0x49d5e9ff).into()),
terminal_ansi_white: Some(rgba(0xb1c9ccff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Uva".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x6d66a7ff).into()),
border_variant: Some(rgba(0x6d66a7ff).into()),
border_focused: Some(rgba(0x6d66a7ff).into()),
border_selected: Some(rgba(0x6d66a7ff).into()),
border_transparent: Some(rgba(0x6d66a7ff).into()),
border_disabled: Some(rgba(0x6d66a7ff).into()),
elevated_surface_background: Some(rgba(0x1f1d30ff).into()),
surface_background: Some(rgba(0x1f1d30ff).into()),
background: Some(rgba(0x292640ff).into()),
element_background: Some(rgba(0x007e99ff).into()),
text: Some(rgba(0xc5c2d6ff).into()),
tab_inactive_background: Some(rgba(0x2f2c49ff).into()),
tab_active_background: Some(rgba(0x292640ff).into()),
terminal_background: Some(rgba(0x1f1d30ff).into()),
terminal_ansi_bright_black: Some(rgba(0x504e65ff).into()),
terminal_ansi_bright_red: Some(rgba(0xe97749ff).into()),
terminal_ansi_bright_green: Some(rgba(0x5febb1ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe69532ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x5fb5ebff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xe697b2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x5fdaebff).into()),
terminal_ansi_bright_white: Some(rgba(0xc5c2d6ff).into()),
terminal_ansi_black: Some(rgba(0x302f3dff).into()),
terminal_ansi_red: Some(rgba(0xe66432ff).into()),
terminal_ansi_green: Some(rgba(0x49e9a6ff).into()),
terminal_ansi_yellow: Some(rgba(0xe4b781ff).into()),
terminal_ansi_blue: Some(rgba(0x49ace9ff).into()),
terminal_ansi_magenta: Some(rgba(0xdf759aff).into()),
terminal_ansi_cyan: Some(rgba(0x49d5e9ff).into()),
terminal_ansi_white: Some(rgba(0xb6b3ccff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Noctis Viola".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x8666a7ff).into()),
border_variant: Some(rgba(0x8666a7ff).into()),
border_focused: Some(rgba(0x8666a7ff).into()),
border_selected: Some(rgba(0x8666a7ff).into()),
border_transparent: Some(rgba(0x8666a7ff).into()),
border_disabled: Some(rgba(0x8666a7ff).into()),
elevated_surface_background: Some(rgba(0x291d35ff).into()),
surface_background: Some(rgba(0x291d35ff).into()),
background: Some(rgba(0x30243dff).into()),
element_background: Some(rgba(0x007e99ff).into()),
text: Some(rgba(0xccbfd9ff).into()),
tab_inactive_background: Some(rgba(0x3d2e4dff).into()),
tab_active_background: Some(rgba(0x30243dff).into()),
terminal_background: Some(rgba(0x291d35ff).into()),
terminal_ansi_bright_black: Some(rgba(0x594e65ff).into()),
terminal_ansi_bright_red: Some(rgba(0xe97749ff).into()),
terminal_ansi_bright_green: Some(rgba(0x5febb1ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xe69532ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x5fb5ebff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xe697b2ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x5fdaebff).into()),
terminal_ansi_bright_white: Some(rgba(0xccbfd9ff).into()),
terminal_ansi_black: Some(rgba(0x362f3dff).into()),
terminal_ansi_red: Some(rgba(0xe66432ff).into()),
terminal_ansi_green: Some(rgba(0x49e9a6ff).into()),
terminal_ansi_yellow: Some(rgba(0xe4b781ff).into()),
terminal_ansi_blue: Some(rgba(0x49ace9ff).into()),
terminal_ansi_magenta: Some(rgba(0xdf759aff).into()),
terminal_ansi_cyan: Some(rgba(0x49d5e9ff).into()),
terminal_ansi_white: Some(rgba(0xbfafcfff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,131 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn palenight() -> UserThemeFamily {
UserThemeFamily {
name: "Palenight".into(),
author: "Olaolu Olawuyi (whizkydee)".into(),
themes: vec![
UserTheme {
name: "Palenight".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x282b3bff).into()),
border_variant: Some(rgba(0x282b3bff).into()),
border_focused: Some(rgba(0x282b3bff).into()),
border_selected: Some(rgba(0x282b3bff).into()),
border_transparent: Some(rgba(0x282b3bff).into()),
border_disabled: Some(rgba(0x282b3bff).into()),
elevated_surface_background: Some(rgba(0x292c3eff).into()),
surface_background: Some(rgba(0x292c3eff).into()),
background: Some(rgba(0x292c3eff).into()),
element_background: Some(rgba(0x7d56c1cc).into()),
text: Some(rgba(0xffffffff).into()),
tab_inactive_background: Some(rgba(0x31364aff).into()),
tab_active_background: Some(rgba(0x292c3eff).into()),
terminal_ansi_bright_black: Some(rgba(0x676e95ff).into()),
terminal_ansi_bright_red: Some(rgba(0xff5571ff).into()),
terminal_ansi_bright_green: Some(rgba(0xc3e88dff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffcb6bff).into()),
terminal_ansi_bright_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x89ddffff).into()),
terminal_ansi_bright_white: Some(rgba(0xffffffff).into()),
terminal_ansi_black: Some(rgba(0x676e95ff).into()),
terminal_ansi_red: Some(rgba(0xff5571ff).into()),
terminal_ansi_green: Some(rgba(0xa9c77dff).into()),
terminal_ansi_yellow: Some(rgba(0xffcb6bff).into()),
terminal_ansi_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_cyan: Some(rgba(0x89ddffff).into()),
terminal_ansi_white: Some(rgba(0xffffffff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Palenight Operator".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x282b3bff).into()),
border_variant: Some(rgba(0x282b3bff).into()),
border_focused: Some(rgba(0x282b3bff).into()),
border_selected: Some(rgba(0x282b3bff).into()),
border_transparent: Some(rgba(0x282b3bff).into()),
border_disabled: Some(rgba(0x282b3bff).into()),
elevated_surface_background: Some(rgba(0x292c3eff).into()),
surface_background: Some(rgba(0x292c3eff).into()),
background: Some(rgba(0x292c3eff).into()),
element_background: Some(rgba(0x7d56c1cc).into()),
text: Some(rgba(0xffffffff).into()),
tab_inactive_background: Some(rgba(0x31364aff).into()),
tab_active_background: Some(rgba(0x292c3eff).into()),
terminal_ansi_bright_black: Some(rgba(0x676e95ff).into()),
terminal_ansi_bright_red: Some(rgba(0xff5571ff).into()),
terminal_ansi_bright_green: Some(rgba(0xc3e88dff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffcb6bff).into()),
terminal_ansi_bright_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x89ddffff).into()),
terminal_ansi_bright_white: Some(rgba(0xffffffff).into()),
terminal_ansi_black: Some(rgba(0x676e95ff).into()),
terminal_ansi_red: Some(rgba(0xff5571ff).into()),
terminal_ansi_green: Some(rgba(0xa9c77dff).into()),
terminal_ansi_yellow: Some(rgba(0xffcb6bff).into()),
terminal_ansi_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_cyan: Some(rgba(0x89ddffff).into()),
terminal_ansi_white: Some(rgba(0xffffffff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Palenight (Mild Contrast)".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x2c2f40ff).into()),
border_variant: Some(rgba(0x2c2f40ff).into()),
border_focused: Some(rgba(0x2c2f40ff).into()),
border_selected: Some(rgba(0x2c2f40ff).into()),
border_transparent: Some(rgba(0x2c2f40ff).into()),
border_disabled: Some(rgba(0x2c2f40ff).into()),
elevated_surface_background: Some(rgba(0x25283aff).into()),
surface_background: Some(rgba(0x25283aff).into()),
background: Some(rgba(0x292c3eff).into()),
element_background: Some(rgba(0x7d56c1cc).into()),
text: Some(rgba(0xffffffff).into()),
tab_inactive_background: Some(rgba(0x31364aff).into()),
tab_active_background: Some(rgba(0x25283aff).into()),
terminal_ansi_bright_black: Some(rgba(0x676e95ff).into()),
terminal_ansi_bright_red: Some(rgba(0xff5571ff).into()),
terminal_ansi_bright_green: Some(rgba(0xc3e88dff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xffcb6bff).into()),
terminal_ansi_bright_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x89ddffff).into()),
terminal_ansi_bright_white: Some(rgba(0xffffffff).into()),
terminal_ansi_black: Some(rgba(0x676e95ff).into()),
terminal_ansi_red: Some(rgba(0xff5571ff).into()),
terminal_ansi_green: Some(rgba(0xa9c77dff).into()),
terminal_ansi_yellow: Some(rgba(0xffcb6bff).into()),
terminal_ansi_blue: Some(rgba(0x82aaffff).into()),
terminal_ansi_magenta: Some(rgba(0xc792eaff).into()),
terminal_ansi_cyan: Some(rgba(0x89ddffff).into()),
terminal_ansi_white: Some(rgba(0xffffffff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,131 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn rose_pine() -> UserThemeFamily {
UserThemeFamily {
name: "Rose Pine".into(),
author: "Rosé Pine".into(),
themes: vec![
UserTheme {
name: "Rose Pine".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x000000ff).into()),
border_variant: Some(rgba(0x000000ff).into()),
border_focused: Some(rgba(0x000000ff).into()),
border_selected: Some(rgba(0x000000ff).into()),
border_transparent: Some(rgba(0x000000ff).into()),
border_disabled: Some(rgba(0x000000ff).into()),
elevated_surface_background: Some(rgba(0x1f1d2eff).into()),
surface_background: Some(rgba(0x1f1d2eff).into()),
background: Some(rgba(0x191724ff).into()),
element_background: Some(rgba(0xebbcbaff).into()),
text: Some(rgba(0xe0def4ff).into()),
tab_inactive_background: Some(rgba(0x000000ff).into()),
tab_active_background: Some(rgba(0x6e6a861a).into()),
terminal_ansi_bright_black: Some(rgba(0x908caaff).into()),
terminal_ansi_bright_red: Some(rgba(0xeb6f92ff).into()),
terminal_ansi_bright_green: Some(rgba(0x30738fff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xf5c177ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x9ccfd8ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xc4a7e7ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0xebbcbaff).into()),
terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
terminal_ansi_black: Some(rgba(0x26233aff).into()),
terminal_ansi_red: Some(rgba(0xeb6f92ff).into()),
terminal_ansi_green: Some(rgba(0x30738fff).into()),
terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
terminal_ansi_blue: Some(rgba(0x9ccfd8ff).into()),
terminal_ansi_magenta: Some(rgba(0xc4a7e7ff).into()),
terminal_ansi_cyan: Some(rgba(0xebbcbaff).into()),
terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Rose Moon".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x000000ff).into()),
border_variant: Some(rgba(0x000000ff).into()),
border_focused: Some(rgba(0x000000ff).into()),
border_selected: Some(rgba(0x000000ff).into()),
border_transparent: Some(rgba(0x000000ff).into()),
border_disabled: Some(rgba(0x000000ff).into()),
elevated_surface_background: Some(rgba(0x2a273eff).into()),
surface_background: Some(rgba(0x2a273eff).into()),
background: Some(rgba(0x232136ff).into()),
element_background: Some(rgba(0xea9a97ff).into()),
text: Some(rgba(0xe0def4ff).into()),
tab_inactive_background: Some(rgba(0x000000ff).into()),
tab_active_background: Some(rgba(0x817c9c14).into()),
terminal_ansi_bright_black: Some(rgba(0x908caaff).into()),
terminal_ansi_bright_red: Some(rgba(0xeb6f92ff).into()),
terminal_ansi_bright_green: Some(rgba(0x3d8fb0ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xf5c177ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x9ccfd8ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xc4a7e7ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0xea9a97ff).into()),
terminal_ansi_bright_white: Some(rgba(0xe0def4ff).into()),
terminal_ansi_black: Some(rgba(0x393552ff).into()),
terminal_ansi_red: Some(rgba(0xeb6f92ff).into()),
terminal_ansi_green: Some(rgba(0x3d8fb0ff).into()),
terminal_ansi_yellow: Some(rgba(0xf5c177ff).into()),
terminal_ansi_blue: Some(rgba(0x9ccfd8ff).into()),
terminal_ansi_magenta: Some(rgba(0xc4a7e7ff).into()),
terminal_ansi_cyan: Some(rgba(0xea9a97ff).into()),
terminal_ansi_white: Some(rgba(0xe0def4ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Rose Pine Dawn".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x000000ff).into()),
border_variant: Some(rgba(0x000000ff).into()),
border_focused: Some(rgba(0x000000ff).into()),
border_selected: Some(rgba(0x000000ff).into()),
border_transparent: Some(rgba(0x000000ff).into()),
border_disabled: Some(rgba(0x000000ff).into()),
elevated_surface_background: Some(rgba(0xfffaf3ff).into()),
surface_background: Some(rgba(0xfffaf3ff).into()),
background: Some(rgba(0xfaf4edff).into()),
element_background: Some(rgba(0xd7827dff).into()),
text: Some(rgba(0x575279ff).into()),
tab_inactive_background: Some(rgba(0x000000ff).into()),
tab_active_background: Some(rgba(0x6e6a860d).into()),
terminal_ansi_bright_black: Some(rgba(0x797593ff).into()),
terminal_ansi_bright_red: Some(rgba(0xb3627aff).into()),
terminal_ansi_bright_green: Some(rgba(0x276983ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xea9d34ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x55949fff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x9079a9ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0xd7827dff).into()),
terminal_ansi_bright_white: Some(rgba(0x575279ff).into()),
terminal_ansi_black: Some(rgba(0xf2e9e1ff).into()),
terminal_ansi_red: Some(rgba(0xb3627aff).into()),
terminal_ansi_green: Some(rgba(0x276983ff).into()),
terminal_ansi_yellow: Some(rgba(0xea9d34ff).into()),
terminal_ansi_blue: Some(rgba(0x55949fff).into()),
terminal_ansi_magenta: Some(rgba(0x9079a9ff).into()),
terminal_ansi_cyan: Some(rgba(0xd7827dff).into()),
terminal_ansi_white: Some(rgba(0x575279ff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,87 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn solarized() -> UserThemeFamily {
UserThemeFamily {
name: "Solarized".into(),
author: "Ethan Schoonover (altercation)".into(),
themes: vec![
UserTheme {
name: "Solarized Dark".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0x003847ff).into()),
border_variant: Some(rgba(0x003847ff).into()),
border_focused: Some(rgba(0x003847ff).into()),
border_selected: Some(rgba(0x003847ff).into()),
border_transparent: Some(rgba(0x003847ff).into()),
border_disabled: Some(rgba(0x003847ff).into()),
background: Some(rgba(0x002a35ff).into()),
element_background: Some(rgba(0x29a19899).into()),
tab_inactive_background: Some(rgba(0x003f51ff).into()),
tab_active_background: Some(rgba(0x002a36ff).into()),
terminal_ansi_bright_black: Some(rgba(0x586e75ff).into()),
terminal_ansi_bright_red: Some(rgba(0xcb4b15ff).into()),
terminal_ansi_bright_green: Some(rgba(0x859900ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0x657b83ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x839496ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x6c71c4ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x93a1a1ff).into()),
terminal_ansi_bright_white: Some(rgba(0x839496ff).into()),
terminal_ansi_black: Some(rgba(0x063642ff).into()),
terminal_ansi_red: Some(rgba(0xdc312eff).into()),
terminal_ansi_green: Some(rgba(0x859900ff).into()),
terminal_ansi_yellow: Some(rgba(0xb58800ff).into()),
terminal_ansi_blue: Some(rgba(0x258ad2ff).into()),
terminal_ansi_magenta: Some(rgba(0xd33582ff).into()),
terminal_ansi_cyan: Some(rgba(0x29a198ff).into()),
terminal_ansi_white: Some(rgba(0x839496ff).into()),
..Default::default()
},
},
},
UserTheme {
name: "Solarized Light".into(),
appearance: Appearance::Light,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
border: Some(rgba(0xddd6c1ff).into()),
border_variant: Some(rgba(0xddd6c1ff).into()),
border_focused: Some(rgba(0xddd6c1ff).into()),
border_selected: Some(rgba(0xddd6c1ff).into()),
border_transparent: Some(rgba(0xddd6c1ff).into()),
border_disabled: Some(rgba(0xddd6c1ff).into()),
background: Some(rgba(0xfdf6e3ff).into()),
element_background: Some(rgba(0xab9d56ff).into()),
tab_inactive_background: Some(rgba(0xd3cbb7ff).into()),
tab_active_background: Some(rgba(0xfdf6e3ff).into()),
terminal_ansi_bright_black: Some(rgba(0x657b83ff).into()),
terminal_ansi_bright_red: Some(rgba(0xcb4b15ff).into()),
terminal_ansi_bright_green: Some(rgba(0x859900ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0x657b83ff).into()),
terminal_ansi_bright_blue: Some(rgba(0x839496ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0x6c71c4ff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x93a1a1ff).into()),
terminal_ansi_bright_white: Some(rgba(0xeee8d5ff).into()),
terminal_ansi_black: Some(rgba(0x657b83ff).into()),
terminal_ansi_red: Some(rgba(0xdc312eff).into()),
terminal_ansi_green: Some(rgba(0x859900ff).into()),
terminal_ansi_yellow: Some(rgba(0xb58800ff).into()),
terminal_ansi_blue: Some(rgba(0x258ad2ff).into()),
terminal_ansi_magenta: Some(rgba(0xd33582ff).into()),
terminal_ansi_cyan: Some(rgba(0x29a198ff).into()),
terminal_ansi_white: Some(rgba(0xeee8d5ff).into()),
..Default::default()
},
},
},
],
}
}

View file

@ -0,0 +1,40 @@
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
pub fn synthwave_84() -> UserThemeFamily {
UserThemeFamily {
name: "Synthwave 84".into(),
author: "Robb Owen (robb0wen)".into(),
themes: vec![UserTheme {
name: "Synthwave 84".into(),
appearance: Appearance::Dark,
styles: UserThemeStylesRefinement {
colors: ThemeColorsRefinement {
background: Some(rgba(0x252334ff).into()),
element_background: Some(rgba(0x614d85ff).into()),
text: Some(rgba(0xffffffff).into()),
tab_inactive_background: Some(rgba(0x252334ff).into()),
terminal_ansi_bright_red: Some(rgba(0xfe444fff).into()),
terminal_ansi_bright_green: Some(rgba(0x71f1b7ff).into()),
terminal_ansi_bright_yellow: Some(rgba(0xfede5cff).into()),
terminal_ansi_bright_blue: Some(rgba(0x02edf9ff).into()),
terminal_ansi_bright_magenta: Some(rgba(0xff7ddaff).into()),
terminal_ansi_bright_cyan: Some(rgba(0x02edf9ff).into()),
terminal_ansi_red: Some(rgba(0xfe444fff).into()),
terminal_ansi_green: Some(rgba(0x71f1b7ff).into()),
terminal_ansi_yellow: Some(rgba(0xf3e70fff).into()),
terminal_ansi_blue: Some(rgba(0x02edf9ff).into()),
terminal_ansi_magenta: Some(rgba(0xff7ddaff).into()),
terminal_ansi_cyan: Some(rgba(0x02edf9ff).into()),
..Default::default()
},
},
}],
}
}

View file

@ -0,0 +1,25 @@
use refineable::Refineable;
use serde::Deserialize;
use crate::{Appearance, ThemeColors, ThemeColorsRefinement};
#[derive(Deserialize)]
pub struct UserThemeFamily {
pub name: String,
pub author: String,
pub themes: Vec<UserTheme>,
}
#[derive(Deserialize)]
pub struct UserTheme {
pub name: String,
pub appearance: Appearance,
pub styles: UserThemeStylesRefinement,
}
#[derive(Refineable, Clone)]
#[refineable(deserialize)]
pub struct UserThemeStyles {
#[refineable]
pub colors: ThemeColors,
}

View file

@ -0,0 +1,18 @@
[package]
name = "theme_importer"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow.workspace = true
convert_case = "0.6.0"
gpui = { package = "gpui2", path = "../gpui2" }
log.workspace = true
rust-embed.workspace = true
serde.workspace = true
simplelog = "0.9"
theme = { package = "theme2", path = "../theme2" }
uuid.workspace = true

View file

@ -0,0 +1,124 @@
# Zed Theme Importer
---
## Usage
- `cargo run -p theme_importer` - Import the context of `assets/themes/src`
---
## Troubleshooting
As the importer generates rust files, you may need to manually do some cleanup in `registry.rs` and `themes/mod.rs` if you remove themes or delete the `themes` folder in the theme crate.
---
## Required Structure
To import a theme or series of themes 3 things are required:
- `family.json`: A JSON file containing the theme family metadata and list of theme variants
- `{theme_name}.json`: One theme json for each theme variant
- `LICENSE`: A license file for the theme family
### `family.json`
#### `name`
The name of the theme family. Avoid special characters.
This will be used for the theme family directory name (lowercased) and the theme family name in the Zed UI.
Good:
- `Rose Pine`
- `Synthwave 84`
- `Monokai Solarized`
Bad:
- `Rosé Pine`
- `Synthwave '84`
- `Monokai (Solarized)`
#### `author`
The author of the theme family. This can be a name or a username.
This will be used for the theme family author in the Zed UI.
#### `themes`
A list of theme variants.
`appearance` can be either `light` or `dark`. This will impact which default fallback colors are used, and where the theme shows up in the Zed UI.
### `{theme_name}.json`
Each theme added to the family must have a corresponding JSON file. This JSON file can be obtained from the VSCode extensions folder (once you have installed it.) This is usually located at `~/.vscode/extensions` (on macOS).
You can use `open ~/.vscode/extensions` to open the folder in Finder directly.
Copy that json file into the theme family directory and tidy up the filenames as needed.
### `LICENSE`
A LICENSE file is required to import a theme family. Failing to provide a complete text license will cause it to be skipped when the import is run.
If the theme only provices a license code (e.g. MIT, Apache 2.0, etc.) then put that code into the LICENSE file.
If no license is provided, either contact the theme creator or don't add the theme.
---
### Complete Example:
An example family with multiple variants:
```json
{
"name": "Ayu",
// When both name and username are available
// prefer the `username (name)` format
"author": "dempfi (Ike Ku)",
"themes": [
{
"name": "Ayu Light",
"file_name": "ayu-light.json",
"appearance": "light"
},
{
"name": "Ayu Mirage",
"file_name": "ayu-mirage.json",
"appearance": "dark"
},
{
"name": "Ayu Dark",
"file_name": "ayu-dark.json",
"appearance": "dark"
}
]
}
```
An example single variant family:
```json
{
"name": "Andromeda",
"author": "Eliver Lara (EliverLara)",
"themes": [
{
"name": "Andromeda",
"file_name": "andromeda.json",
"appearance": "dark"
},
{
"name": "Andromeda Bordered",
"file_name": "andromeda-bordered.json",
"appearance": "dark"
}
]
}
```

View file

@ -0,0 +1,237 @@
mod theme_printer;
mod util;
mod vscode;
use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use anyhow::{anyhow, Context, Result};
use convert_case::{Case, Casing};
use gpui::serde_json;
use log::LevelFilter;
use serde::Deserialize;
use simplelog::SimpleLogger;
use theme::{Appearance, UserThemeFamily};
use vscode::VsCodeThemeConverter;
use crate::theme_printer::UserThemeFamilyPrinter;
use crate::vscode::VsCodeTheme;
#[derive(Debug, Deserialize)]
struct FamilyMetadata {
pub name: String,
pub author: String,
pub themes: Vec<ThemeMetadata>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ThemeAppearanceJson {
Light,
Dark,
}
impl From<ThemeAppearanceJson> for Appearance {
fn from(value: ThemeAppearanceJson) -> Self {
match value {
ThemeAppearanceJson::Light => Self::Light,
ThemeAppearanceJson::Dark => Self::Dark,
}
}
}
#[derive(Debug, Deserialize)]
pub struct ThemeMetadata {
pub name: String,
pub file_name: String,
pub appearance: ThemeAppearanceJson,
}
fn main() -> Result<()> {
const SOURCE_PATH: &str = "assets/themes/src/vscode";
const OUT_PATH: &str = "crates/theme2/src/themes";
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
println!("Loading themes source...");
let vscode_themes_path = PathBuf::from_str(SOURCE_PATH)?;
if !vscode_themes_path.exists() {
return Err(anyhow!(format!(
"Couldn't find {}, make sure it exists",
SOURCE_PATH
)));
}
let mut theme_families = Vec::new();
for theme_family_dir in fs::read_dir(&vscode_themes_path)? {
let theme_family_dir = theme_family_dir?;
if !theme_family_dir.file_type()?.is_dir() {
continue;
}
let theme_family_slug = theme_family_dir
.path()
.file_stem()
.ok_or(anyhow!("no file stem"))
.map(|stem| stem.to_string_lossy().to_string())?;
let family_metadata_file = File::open(theme_family_dir.path().join("family.json"))
.context(format!(
"no `family.json` found for '{}'",
theme_family_slug
))?;
let license_file_path = theme_family_dir.path().join("LICENSE");
if !license_file_path.exists() {
println!("Skipping theme family '{}' because it does not have a LICENSE file. This theme will only be imported once a LICENSE file is provided.", theme_family_slug);
continue;
}
let family_metadata: FamilyMetadata = serde_json::from_reader(family_metadata_file)
.context(format!(
"failed to parse `family.json` for '{theme_family_slug}'"
))?;
let mut themes = Vec::new();
for theme_metadata in family_metadata.themes {
let theme_file_path = theme_family_dir.path().join(&theme_metadata.file_name);
let theme_file = match File::open(&theme_file_path) {
Ok(file) => file,
Err(_) => {
println!("Failed to open file at path: {:?}", theme_file_path);
continue;
}
};
let vscode_theme: VsCodeTheme = serde_json::from_reader(theme_file)
.context(format!("failed to parse theme {theme_file_path:?}"))?;
let converter = VsCodeThemeConverter::new(vscode_theme, theme_metadata);
let theme = converter.convert()?;
themes.push(theme);
}
let theme_family = UserThemeFamily {
name: family_metadata.name.into(),
author: family_metadata.author.into(),
themes,
};
theme_families.push(theme_family);
}
let themes_output_path = PathBuf::from_str(OUT_PATH)?;
if !themes_output_path.exists() {
println!("Creating directory: {:?}", themes_output_path);
fs::create_dir_all(&themes_output_path)?;
}
let mut mod_rs_file = File::create(themes_output_path.join(format!("mod.rs")))?;
let mut theme_modules = Vec::new();
for theme_family in theme_families {
let theme_family_slug = theme_family.name.to_string().to_case(Case::Snake);
let mut output_file =
File::create(themes_output_path.join(format!("{theme_family_slug}.rs")))?;
println!(
"Creating file: {:?}",
themes_output_path.join(format!("{theme_family_slug}.rs"))
);
let theme_module = format!(
r#"
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
use gpui::rgba;
use crate::{{
Appearance, ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
}};
pub fn {theme_family_slug}() -> UserThemeFamily {{
{theme_family_definition}
}}
"#,
theme_family_definition = format!("{:#?}", UserThemeFamilyPrinter::new(theme_family))
);
output_file.write_all(theme_module.as_bytes())?;
theme_modules.push(theme_family_slug);
}
let themes_vector_contents = format!(
r#"
use crate::UserThemeFamily;
pub(crate) fn all_user_themes() -> Vec<UserThemeFamily> {{
vec![{all_themes}]
}}
"#,
all_themes = theme_modules
.iter()
.map(|module| format!("{}()", module))
.collect::<Vec<_>>()
.join(", ")
);
let mod_rs_contents = format!(
r#"
// This file was generated by the `theme_importer`.
// Be careful when modifying it by hand.
{mod_statements}
{use_statements}
{themes_vector_contents}
"#,
mod_statements = theme_modules
.iter()
.map(|module| format!("mod {module};"))
.collect::<Vec<_>>()
.join("\n"),
use_statements = theme_modules
.iter()
.map(|module| format!("pub use {module}::*;"))
.collect::<Vec<_>>()
.join("\n"),
themes_vector_contents = themes_vector_contents
);
mod_rs_file.write_all(mod_rs_contents.as_bytes())?;
println!("Formatting themes...");
let format_result = format_themes_crate()
// We need to format a second time to catch all of the formatting issues.
.and_then(|_| format_themes_crate());
if let Err(err) = format_result {
eprintln!("Failed to format themes: {}", err);
}
println!("Done!");
Ok(())
}
fn format_themes_crate() -> std::io::Result<std::process::Output> {
Command::new("cargo")
.args(["fmt", "--package", "theme2"])
.output()
}

View file

@ -0,0 +1,322 @@
use std::fmt::{self, Debug};
use gpui::{Hsla, Rgba};
use theme::{
Appearance, PlayerColor, PlayerColors, StatusColors, SyntaxTheme, SystemColors,
ThemeColorsRefinement, UserTheme, UserThemeFamily, UserThemeStylesRefinement,
};
struct RawSyntaxPrinter<'a>(&'a str);
impl<'a> Debug for RawSyntaxPrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
struct HslaPrinter(Hsla);
impl Debug for HslaPrinter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", IntoPrinter(&Rgba::from(self.0)))
}
}
struct IntoPrinter<'a, D: Debug>(&'a D);
impl<'a, D: Debug> Debug for IntoPrinter<'a, D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}.into()", self.0)
}
}
pub struct VecPrinter<'a, T>(&'a Vec<T>);
impl<'a, T: Debug> Debug for VecPrinter<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "vec!{:?}", &self.0)
}
}
pub struct UserThemeFamilyPrinter(UserThemeFamily);
impl UserThemeFamilyPrinter {
pub fn new(theme_family: UserThemeFamily) -> Self {
Self(theme_family)
}
}
impl Debug for UserThemeFamilyPrinter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UserThemeFamily")
.field("name", &IntoPrinter(&self.0.name))
.field("author", &IntoPrinter(&self.0.author))
.field(
"themes",
&VecPrinter(
&self
.0
.themes
.iter()
.map(|theme| UserThemePrinter(theme))
.collect(),
),
)
.finish()
}
}
pub struct UserThemePrinter<'a>(&'a UserTheme);
impl<'a> Debug for UserThemePrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UserTheme")
.field("name", &IntoPrinter(&self.0.name))
.field("appearance", &AppearancePrinter(self.0.appearance))
.field("styles", &UserThemeStylesRefinementPrinter(&self.0.styles))
.finish()
}
}
pub struct AppearancePrinter(Appearance);
impl Debug for AppearancePrinter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Appearance::{:?}", self.0)
}
}
pub struct UserThemeStylesRefinementPrinter<'a>(&'a UserThemeStylesRefinement);
impl<'a> Debug for UserThemeStylesRefinementPrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UserThemeStylesRefinement")
.field("colors", &ThemeColorsRefinementPrinter(&self.0.colors))
.finish()
}
}
pub struct SystemColorsPrinter<'a>(&'a SystemColors);
impl<'a> Debug for SystemColorsPrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemColors")
.field("transparent", &HslaPrinter(self.0.transparent))
.field(
"mac_os_traffic_light_red",
&HslaPrinter(self.0.mac_os_traffic_light_red),
)
.field(
"mac_os_traffic_light_yellow",
&HslaPrinter(self.0.mac_os_traffic_light_yellow),
)
.field(
"mac_os_traffic_light_green",
&HslaPrinter(self.0.mac_os_traffic_light_green),
)
.finish()
}
}
pub struct ThemeColorsRefinementPrinter<'a>(&'a ThemeColorsRefinement);
impl<'a> Debug for ThemeColorsRefinementPrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let theme_colors = vec![
("border", self.0.border),
("border_variant", self.0.border_variant),
("border_focused", self.0.border_focused),
("border_selected", self.0.border_selected),
("border_transparent", self.0.border_transparent),
("border_disabled", self.0.border_disabled),
(
"elevated_surface_background",
self.0.elevated_surface_background,
),
("surface_background", self.0.surface_background),
("background", self.0.background),
("element_background", self.0.element_background),
("element_hover", self.0.element_hover),
("element_active", self.0.element_active),
("element_selected", self.0.element_selected),
("element_disabled", self.0.element_disabled),
("drop_target_background", self.0.drop_target_background),
("ghost_element_background", self.0.ghost_element_background),
("ghost_element_hover", self.0.ghost_element_hover),
("ghost_element_active", self.0.ghost_element_active),
("ghost_element_selected", self.0.ghost_element_selected),
("ghost_element_disabled", self.0.ghost_element_disabled),
("text", self.0.text),
("text_muted", self.0.text_muted),
("text_placeholder", self.0.text_placeholder),
("text_disabled", self.0.text_disabled),
("text_accent", self.0.text_accent),
("icon", self.0.icon),
("icon_muted", self.0.icon_muted),
("icon_disabled", self.0.icon_disabled),
("icon_placeholder", self.0.icon_placeholder),
("icon_accent", self.0.icon_accent),
("status_bar_background", self.0.status_bar_background),
("title_bar_background", self.0.title_bar_background),
("toolbar_background", self.0.toolbar_background),
("tab_bar_background", self.0.tab_bar_background),
("tab_inactive_background", self.0.tab_inactive_background),
("tab_active_background", self.0.tab_active_background),
("editor_background", self.0.editor_background),
("editor_gutter_background", self.0.editor_gutter_background),
(
"editor_subheader_background",
self.0.editor_subheader_background,
),
(
"editor_active_line_background",
self.0.editor_active_line_background,
),
(
"editor_highlighted_line_background",
self.0.editor_highlighted_line_background,
),
("editor_line_number", self.0.editor_line_number),
(
"editor_active_line_number",
self.0.editor_active_line_number,
),
("editor_invisible", self.0.editor_invisible),
("editor_wrap_guide", self.0.editor_wrap_guide),
("editor_active_wrap_guide", self.0.editor_active_wrap_guide),
(
"editor_document_highlight_read_background",
self.0.editor_document_highlight_read_background,
),
(
"editor_document_highlight_write_background",
self.0.editor_document_highlight_write_background,
),
("terminal_background", self.0.terminal_background),
(
"terminal_ansi_bright_black",
self.0.terminal_ansi_bright_black,
),
("terminal_ansi_bright_red", self.0.terminal_ansi_bright_red),
(
"terminal_ansi_bright_green",
self.0.terminal_ansi_bright_green,
),
(
"terminal_ansi_bright_yellow",
self.0.terminal_ansi_bright_yellow,
),
(
"terminal_ansi_bright_blue",
self.0.terminal_ansi_bright_blue,
),
(
"terminal_ansi_bright_magenta",
self.0.terminal_ansi_bright_magenta,
),
(
"terminal_ansi_bright_cyan",
self.0.terminal_ansi_bright_cyan,
),
(
"terminal_ansi_bright_white",
self.0.terminal_ansi_bright_white,
),
("terminal_ansi_black", self.0.terminal_ansi_black),
("terminal_ansi_red", self.0.terminal_ansi_red),
("terminal_ansi_green", self.0.terminal_ansi_green),
("terminal_ansi_yellow", self.0.terminal_ansi_yellow),
("terminal_ansi_blue", self.0.terminal_ansi_blue),
("terminal_ansi_magenta", self.0.terminal_ansi_magenta),
("terminal_ansi_cyan", self.0.terminal_ansi_cyan),
("terminal_ansi_white", self.0.terminal_ansi_white),
];
f.write_str("ThemeColorsRefinement {")?;
for (color_name, color) in theme_colors {
if let Some(color) = color {
f.write_str(color_name)?;
f.write_str(": ")?;
f.write_str("Some(")?;
HslaPrinter(color).fmt(f)?;
f.write_str(")")?;
f.write_str(",")?;
}
}
f.write_str("..Default::default()")?;
f.write_str("}")
}
}
pub struct StatusColorsPrinter<'a>(&'a StatusColors);
impl<'a> Debug for StatusColorsPrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StatusColors")
.field("conflict", &HslaPrinter(self.0.conflict))
.field("created", &HslaPrinter(self.0.created))
.field("deleted", &HslaPrinter(self.0.deleted))
.field("error", &HslaPrinter(self.0.error))
.field("hidden", &HslaPrinter(self.0.hidden))
.field("ignored", &HslaPrinter(self.0.ignored))
.field("info", &HslaPrinter(self.0.info))
.field("modified", &HslaPrinter(self.0.modified))
.field("renamed", &HslaPrinter(self.0.renamed))
.field("success", &HslaPrinter(self.0.success))
.field("warning", &HslaPrinter(self.0.warning))
.finish()
}
}
pub struct PlayerColorsPrinter<'a>(&'a PlayerColors);
impl<'a> Debug for PlayerColorsPrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PlayerColors")
.field(&VecPrinter(
&self
.0
.0
.iter()
.map(|player_color| PlayerColorPrinter(player_color))
.collect(),
))
.finish()
}
}
pub struct PlayerColorPrinter<'a>(&'a PlayerColor);
impl<'a> Debug for PlayerColorPrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PlayerColor")
.field("cursor", &HslaPrinter(self.0.cursor))
.field("background", &HslaPrinter(self.0.background))
.field("selection", &HslaPrinter(self.0.selection))
.finish()
}
}
pub struct SyntaxThemePrinter<'a>(&'a SyntaxTheme);
impl<'a> Debug for SyntaxThemePrinter<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SyntaxTheme")
.field(
"highlights",
&VecPrinter(
&self
.0
.highlights
.iter()
.map(|(token, highlight)| {
(IntoPrinter(token), HslaPrinter(highlight.color.unwrap()))
})
.collect(),
),
)
.finish()
}
}

View file

@ -0,0 +1,11 @@
use anyhow::Result;
pub trait Traverse<T, U> {
fn traverse(self, f: impl FnOnce(T) -> Result<U>) -> Result<Option<U>>;
}
impl<T, U> Traverse<T, U> for Option<T> {
fn traverse(self, f: impl FnOnce(T) -> Result<U>) -> Result<Option<U>> {
self.map_or(Ok(None), |value| f(value).map(Some))
}
}

View file

@ -0,0 +1,570 @@
use anyhow::Result;
use gpui::{Hsla, Rgba};
use serde::Deserialize;
use theme::{ThemeColorsRefinement, UserTheme, UserThemeStylesRefinement};
use crate::util::Traverse;
use crate::ThemeMetadata;
#[derive(Deserialize, Debug)]
pub struct VsCodeTheme {
#[serde(rename = "$schema")]
pub schema: Option<String>,
pub name: Option<String>,
pub author: Option<String>,
pub maintainers: Option<Vec<String>>,
#[serde(rename = "semanticClass")]
pub semantic_class: Option<String>,
#[serde(rename = "semanticHighlighting")]
pub semantic_highlighting: Option<bool>,
pub colors: VsCodeColors,
}
#[derive(Debug, Deserialize)]
pub struct VsCodeColors {
#[serde(rename = "terminal.background")]
pub terminal_background: Option<String>,
#[serde(rename = "terminal.foreground")]
pub terminal_foreground: Option<String>,
#[serde(rename = "terminal.ansiBrightBlack")]
pub terminal_ansi_bright_black: Option<String>,
#[serde(rename = "terminal.ansiBrightRed")]
pub terminal_ansi_bright_red: Option<String>,
#[serde(rename = "terminal.ansiBrightGreen")]
pub terminal_ansi_bright_green: Option<String>,
#[serde(rename = "terminal.ansiBrightYellow")]
pub terminal_ansi_bright_yellow: Option<String>,
#[serde(rename = "terminal.ansiBrightBlue")]
pub terminal_ansi_bright_blue: Option<String>,
#[serde(rename = "terminal.ansiBrightMagenta")]
pub terminal_ansi_bright_magenta: Option<String>,
#[serde(rename = "terminal.ansiBrightCyan")]
pub terminal_ansi_bright_cyan: Option<String>,
#[serde(rename = "terminal.ansiBrightWhite")]
pub terminal_ansi_bright_white: Option<String>,
#[serde(rename = "terminal.ansiBlack")]
pub terminal_ansi_black: Option<String>,
#[serde(rename = "terminal.ansiRed")]
pub terminal_ansi_red: Option<String>,
#[serde(rename = "terminal.ansiGreen")]
pub terminal_ansi_green: Option<String>,
#[serde(rename = "terminal.ansiYellow")]
pub terminal_ansi_yellow: Option<String>,
#[serde(rename = "terminal.ansiBlue")]
pub terminal_ansi_blue: Option<String>,
#[serde(rename = "terminal.ansiMagenta")]
pub terminal_ansi_magenta: Option<String>,
#[serde(rename = "terminal.ansiCyan")]
pub terminal_ansi_cyan: Option<String>,
#[serde(rename = "terminal.ansiWhite")]
pub terminal_ansi_white: Option<String>,
#[serde(rename = "focusBorder")]
pub focus_border: Option<String>,
pub foreground: Option<String>,
#[serde(rename = "selection.background")]
pub selection_background: Option<String>,
#[serde(rename = "errorForeground")]
pub error_foreground: Option<String>,
#[serde(rename = "button.background")]
pub button_background: Option<String>,
#[serde(rename = "button.foreground")]
pub button_foreground: Option<String>,
#[serde(rename = "button.secondaryBackground")]
pub button_secondary_background: Option<String>,
#[serde(rename = "button.secondaryForeground")]
pub button_secondary_foreground: Option<String>,
#[serde(rename = "button.secondaryHoverBackground")]
pub button_secondary_hover_background: Option<String>,
#[serde(rename = "dropdown.background")]
pub dropdown_background: Option<String>,
#[serde(rename = "dropdown.border")]
pub dropdown_border: Option<String>,
#[serde(rename = "dropdown.foreground")]
pub dropdown_foreground: Option<String>,
#[serde(rename = "input.background")]
pub input_background: Option<String>,
#[serde(rename = "input.foreground")]
pub input_foreground: Option<String>,
#[serde(rename = "input.border")]
pub input_border: Option<String>,
#[serde(rename = "input.placeholderForeground")]
pub input_placeholder_foreground: Option<String>,
#[serde(rename = "inputOption.activeBorder")]
pub input_option_active_border: Option<String>,
#[serde(rename = "inputValidation.infoBorder")]
pub input_validation_info_border: Option<String>,
#[serde(rename = "inputValidation.warningBorder")]
pub input_validation_warning_border: Option<String>,
#[serde(rename = "inputValidation.errorBorder")]
pub input_validation_error_border: Option<String>,
#[serde(rename = "badge.foreground")]
pub badge_foreground: Option<String>,
#[serde(rename = "badge.background")]
pub badge_background: Option<String>,
#[serde(rename = "progressBar.background")]
pub progress_bar_background: Option<String>,
#[serde(rename = "list.activeSelectionBackground")]
pub list_active_selection_background: Option<String>,
#[serde(rename = "list.activeSelectionForeground")]
pub list_active_selection_foreground: Option<String>,
#[serde(rename = "list.dropBackground")]
pub list_drop_background: Option<String>,
#[serde(rename = "list.focusBackground")]
pub list_focus_background: Option<String>,
#[serde(rename = "list.highlightForeground")]
pub list_highlight_foreground: Option<String>,
#[serde(rename = "list.hoverBackground")]
pub list_hover_background: Option<String>,
#[serde(rename = "list.inactiveSelectionBackground")]
pub list_inactive_selection_background: Option<String>,
#[serde(rename = "list.warningForeground")]
pub list_warning_foreground: Option<String>,
#[serde(rename = "list.errorForeground")]
pub list_error_foreground: Option<String>,
#[serde(rename = "activityBar.background")]
pub activity_bar_background: Option<String>,
#[serde(rename = "activityBar.inactiveForeground")]
pub activity_bar_inactive_foreground: Option<String>,
#[serde(rename = "activityBar.foreground")]
pub activity_bar_foreground: Option<String>,
#[serde(rename = "activityBar.activeBorder")]
pub activity_bar_active_border: Option<String>,
#[serde(rename = "activityBar.activeBackground")]
pub activity_bar_active_background: Option<String>,
#[serde(rename = "activityBarBadge.background")]
pub activity_bar_badge_background: Option<String>,
#[serde(rename = "activityBarBadge.foreground")]
pub activity_bar_badge_foreground: Option<String>,
#[serde(rename = "sideBar.background")]
pub side_bar_background: Option<String>,
#[serde(rename = "sideBarTitle.foreground")]
pub side_bar_title_foreground: Option<String>,
#[serde(rename = "sideBarSectionHeader.background")]
pub side_bar_section_header_background: Option<String>,
#[serde(rename = "sideBarSectionHeader.border")]
pub side_bar_section_header_border: Option<String>,
#[serde(rename = "editorGroup.border")]
pub editor_group_border: Option<String>,
#[serde(rename = "editorGroup.dropBackground")]
pub editor_group_drop_background: Option<String>,
#[serde(rename = "editorGroupHeader.tabsBackground")]
pub editor_group_header_tabs_background: Option<String>,
#[serde(rename = "tab.activeBackground")]
pub tab_active_background: Option<String>,
#[serde(rename = "tab.activeForeground")]
pub tab_active_foreground: Option<String>,
#[serde(rename = "tab.border")]
pub tab_border: Option<String>,
#[serde(rename = "tab.activeBorderTop")]
pub tab_active_border_top: Option<String>,
#[serde(rename = "tab.inactiveBackground")]
pub tab_inactive_background: Option<String>,
#[serde(rename = "tab.inactiveForeground")]
pub tab_inactive_foreground: Option<String>,
#[serde(rename = "editor.foreground")]
pub editor_foreground: Option<String>,
#[serde(rename = "editor.background")]
pub editor_background: Option<String>,
#[serde(rename = "editorLineNumber.foreground")]
pub editor_line_number_foreground: Option<String>,
#[serde(rename = "editor.selectionBackground")]
pub editor_selection_background: Option<String>,
#[serde(rename = "editor.selectionHighlightBackground")]
pub editor_selection_highlight_background: Option<String>,
#[serde(rename = "editor.foldBackground")]
pub editor_fold_background: Option<String>,
#[serde(rename = "editor.wordHighlightBackground")]
pub editor_word_highlight_background: Option<String>,
#[serde(rename = "editor.wordHighlightStrongBackground")]
pub editor_word_highlight_strong_background: Option<String>,
#[serde(rename = "editor.findMatchBackground")]
pub editor_find_match_background: Option<String>,
#[serde(rename = "editor.findMatchHighlightBackground")]
pub editor_find_match_highlight_background: Option<String>,
#[serde(rename = "editor.findRangeHighlightBackground")]
pub editor_find_range_highlight_background: Option<String>,
#[serde(rename = "editor.hoverHighlightBackground")]
pub editor_hover_highlight_background: Option<String>,
#[serde(rename = "editor.lineHighlightBorder")]
pub editor_line_highlight_border: Option<String>,
#[serde(rename = "editorLink.activeForeground")]
pub editor_link_active_foreground: Option<String>,
#[serde(rename = "editor.rangeHighlightBackground")]
pub editor_range_highlight_background: Option<String>,
#[serde(rename = "editor.snippetTabstopHighlightBackground")]
pub editor_snippet_tabstop_highlight_background: Option<String>,
#[serde(rename = "editor.snippetTabstopHighlightBorder")]
pub editor_snippet_tabstop_highlight_border: Option<String>,
#[serde(rename = "editor.snippetFinalTabstopHighlightBackground")]
pub editor_snippet_final_tabstop_highlight_background: Option<String>,
#[serde(rename = "editor.snippetFinalTabstopHighlightBorder")]
pub editor_snippet_final_tabstop_highlight_border: Option<String>,
#[serde(rename = "editorWhitespace.foreground")]
pub editor_whitespace_foreground: Option<String>,
#[serde(rename = "editorIndentGuide.background")]
pub editor_indent_guide_background: Option<String>,
#[serde(rename = "editorIndentGuide.activeBackground")]
pub editor_indent_guide_active_background: Option<String>,
#[serde(rename = "editorRuler.foreground")]
pub editor_ruler_foreground: Option<String>,
#[serde(rename = "editorCodeLens.foreground")]
pub editor_code_lens_foreground: Option<String>,
#[serde(rename = "editorBracketHighlight.foreground1")]
pub editor_bracket_highlight_foreground1: Option<String>,
#[serde(rename = "editorBracketHighlight.foreground2")]
pub editor_bracket_highlight_foreground2: Option<String>,
#[serde(rename = "editorBracketHighlight.foreground3")]
pub editor_bracket_highlight_foreground3: Option<String>,
#[serde(rename = "editorBracketHighlight.foreground4")]
pub editor_bracket_highlight_foreground4: Option<String>,
#[serde(rename = "editorBracketHighlight.foreground5")]
pub editor_bracket_highlight_foreground5: Option<String>,
#[serde(rename = "editorBracketHighlight.foreground6")]
pub editor_bracket_highlight_foreground6: Option<String>,
#[serde(rename = "editorBracketHighlight.unexpectedBracket.foreground")]
pub editor_bracket_highlight_unexpected_bracket_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.border")]
pub editor_overview_ruler_border: Option<String>,
#[serde(rename = "editorOverviewRuler.selectionHighlightForeground")]
pub editor_overview_ruler_selection_highlight_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.wordHighlightForeground")]
pub editor_overview_ruler_word_highlight_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.wordHighlightStrongForeground")]
pub editor_overview_ruler_word_highlight_strong_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.modifiedForeground")]
pub editor_overview_ruler_modified_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.addedForeground")]
pub editor_overview_ruler_added_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.deletedForeground")]
pub editor_overview_ruler_deleted_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.errorForeground")]
pub editor_overview_ruler_error_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.warningForeground")]
pub editor_overview_ruler_warning_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.infoForeground")]
pub editor_overview_ruler_info_foreground: Option<String>,
#[serde(rename = "editorError.foreground")]
pub editor_error_foreground: Option<String>,
#[serde(rename = "editorWarning.foreground")]
pub editor_warning_foreground: Option<String>,
#[serde(rename = "editorGutter.modifiedBackground")]
pub editor_gutter_modified_background: Option<String>,
#[serde(rename = "editorGutter.addedBackground")]
pub editor_gutter_added_background: Option<String>,
#[serde(rename = "editorGutter.deletedBackground")]
pub editor_gutter_deleted_background: Option<String>,
#[serde(rename = "gitDecoration.modifiedResourceForeground")]
pub git_decoration_modified_resource_foreground: Option<String>,
#[serde(rename = "gitDecoration.deletedResourceForeground")]
pub git_decoration_deleted_resource_foreground: Option<String>,
#[serde(rename = "gitDecoration.untrackedResourceForeground")]
pub git_decoration_untracked_resource_foreground: Option<String>,
#[serde(rename = "gitDecoration.ignoredResourceForeground")]
pub git_decoration_ignored_resource_foreground: Option<String>,
#[serde(rename = "gitDecoration.conflictingResourceForeground")]
pub git_decoration_conflicting_resource_foreground: Option<String>,
#[serde(rename = "diffEditor.insertedTextBackground")]
pub diff_editor_inserted_text_background: Option<String>,
#[serde(rename = "diffEditor.removedTextBackground")]
pub diff_editor_removed_text_background: Option<String>,
#[serde(rename = "inlineChat.regionHighlight")]
pub inline_chat_region_highlight: Option<String>,
#[serde(rename = "editorWidget.background")]
pub editor_widget_background: Option<String>,
#[serde(rename = "editorSuggestWidget.background")]
pub editor_suggest_widget_background: Option<String>,
#[serde(rename = "editorSuggestWidget.foreground")]
pub editor_suggest_widget_foreground: Option<String>,
#[serde(rename = "editorSuggestWidget.selectedBackground")]
pub editor_suggest_widget_selected_background: Option<String>,
#[serde(rename = "editorHoverWidget.background")]
pub editor_hover_widget_background: Option<String>,
#[serde(rename = "editorHoverWidget.border")]
pub editor_hover_widget_border: Option<String>,
#[serde(rename = "editorMarkerNavigation.background")]
pub editor_marker_navigation_background: Option<String>,
#[serde(rename = "peekView.border")]
pub peek_view_border: Option<String>,
#[serde(rename = "peekViewEditor.background")]
pub peek_view_editor_background: Option<String>,
#[serde(rename = "peekViewEditor.matchHighlightBackground")]
pub peek_view_editor_match_highlight_background: Option<String>,
#[serde(rename = "peekViewResult.background")]
pub peek_view_result_background: Option<String>,
#[serde(rename = "peekViewResult.fileForeground")]
pub peek_view_result_file_foreground: Option<String>,
#[serde(rename = "peekViewResult.lineForeground")]
pub peek_view_result_line_foreground: Option<String>,
#[serde(rename = "peekViewResult.matchHighlightBackground")]
pub peek_view_result_match_highlight_background: Option<String>,
#[serde(rename = "peekViewResult.selectionBackground")]
pub peek_view_result_selection_background: Option<String>,
#[serde(rename = "peekViewResult.selectionForeground")]
pub peek_view_result_selection_foreground: Option<String>,
#[serde(rename = "peekViewTitle.background")]
pub peek_view_title_background: Option<String>,
#[serde(rename = "peekViewTitleDescription.foreground")]
pub peek_view_title_description_foreground: Option<String>,
#[serde(rename = "peekViewTitleLabel.foreground")]
pub peek_view_title_label_foreground: Option<String>,
#[serde(rename = "merge.currentHeaderBackground")]
pub merge_current_header_background: Option<String>,
#[serde(rename = "merge.incomingHeaderBackground")]
pub merge_incoming_header_background: Option<String>,
#[serde(rename = "editorOverviewRuler.currentContentForeground")]
pub editor_overview_ruler_current_content_foreground: Option<String>,
#[serde(rename = "editorOverviewRuler.incomingContentForeground")]
pub editor_overview_ruler_incoming_content_foreground: Option<String>,
#[serde(rename = "panel.background")]
pub panel_background: Option<String>,
#[serde(rename = "panel.border")]
pub panel_border: Option<String>,
#[serde(rename = "panelTitle.activeBorder")]
pub panel_title_active_border: Option<String>,
#[serde(rename = "panelTitle.activeForeground")]
pub panel_title_active_foreground: Option<String>,
#[serde(rename = "panelTitle.inactiveForeground")]
pub panel_title_inactive_foreground: Option<String>,
#[serde(rename = "statusBar.background")]
pub status_bar_background: Option<String>,
#[serde(rename = "statusBar.foreground")]
pub status_bar_foreground: Option<String>,
#[serde(rename = "statusBar.debuggingBackground")]
pub status_bar_debugging_background: Option<String>,
#[serde(rename = "statusBar.debuggingForeground")]
pub status_bar_debugging_foreground: Option<String>,
#[serde(rename = "statusBar.noFolderBackground")]
pub status_bar_no_folder_background: Option<String>,
#[serde(rename = "statusBar.noFolderForeground")]
pub status_bar_no_folder_foreground: Option<String>,
#[serde(rename = "statusBarItem.prominentBackground")]
pub status_bar_item_prominent_background: Option<String>,
#[serde(rename = "statusBarItem.prominentHoverBackground")]
pub status_bar_item_prominent_hover_background: Option<String>,
#[serde(rename = "statusBarItem.remoteForeground")]
pub status_bar_item_remote_foreground: Option<String>,
#[serde(rename = "statusBarItem.remoteBackground")]
pub status_bar_item_remote_background: Option<String>,
#[serde(rename = "titleBar.activeBackground")]
pub title_bar_active_background: Option<String>,
#[serde(rename = "titleBar.activeForeground")]
pub title_bar_active_foreground: Option<String>,
#[serde(rename = "titleBar.inactiveBackground")]
pub title_bar_inactive_background: Option<String>,
#[serde(rename = "titleBar.inactiveForeground")]
pub title_bar_inactive_foreground: Option<String>,
#[serde(rename = "extensionButton.prominentForeground")]
pub extension_button_prominent_foreground: Option<String>,
#[serde(rename = "extensionButton.prominentBackground")]
pub extension_button_prominent_background: Option<String>,
#[serde(rename = "extensionButton.prominentHoverBackground")]
pub extension_button_prominent_hover_background: Option<String>,
#[serde(rename = "pickerGroup.border")]
pub picker_group_border: Option<String>,
#[serde(rename = "pickerGroup.foreground")]
pub picker_group_foreground: Option<String>,
#[serde(rename = "debugToolBar.background")]
pub debug_tool_bar_background: Option<String>,
#[serde(rename = "walkThrough.embeddedEditorBackground")]
pub walk_through_embedded_editor_background: Option<String>,
#[serde(rename = "settings.headerForeground")]
pub settings_header_foreground: Option<String>,
#[serde(rename = "settings.modifiedItemIndicator")]
pub settings_modified_item_indicator: Option<String>,
#[serde(rename = "settings.dropdownBackground")]
pub settings_dropdown_background: Option<String>,
#[serde(rename = "settings.dropdownForeground")]
pub settings_dropdown_foreground: Option<String>,
#[serde(rename = "settings.dropdownBorder")]
pub settings_dropdown_border: Option<String>,
#[serde(rename = "settings.checkboxBackground")]
pub settings_checkbox_background: Option<String>,
#[serde(rename = "settings.checkboxForeground")]
pub settings_checkbox_foreground: Option<String>,
#[serde(rename = "settings.checkboxBorder")]
pub settings_checkbox_border: Option<String>,
#[serde(rename = "settings.textInputBackground")]
pub settings_text_input_background: Option<String>,
#[serde(rename = "settings.textInputForeground")]
pub settings_text_input_foreground: Option<String>,
#[serde(rename = "settings.textInputBorder")]
pub settings_text_input_border: Option<String>,
#[serde(rename = "settings.numberInputBackground")]
pub settings_number_input_background: Option<String>,
#[serde(rename = "settings.numberInputForeground")]
pub settings_number_input_foreground: Option<String>,
#[serde(rename = "settings.numberInputBorder")]
pub settings_number_input_border: Option<String>,
#[serde(rename = "breadcrumb.foreground")]
pub breadcrumb_foreground: Option<String>,
#[serde(rename = "breadcrumb.background")]
pub breadcrumb_background: Option<String>,
#[serde(rename = "breadcrumb.focusForeground")]
pub breadcrumb_focus_foreground: Option<String>,
#[serde(rename = "breadcrumb.activeSelectionForeground")]
pub breadcrumb_active_selection_foreground: Option<String>,
#[serde(rename = "breadcrumbPicker.background")]
pub breadcrumb_picker_background: Option<String>,
#[serde(rename = "listFilterWidget.background")]
pub list_filter_widget_background: Option<String>,
#[serde(rename = "listFilterWidget.outline")]
pub list_filter_widget_outline: Option<String>,
#[serde(rename = "listFilterWidget.noMatchesOutline")]
pub list_filter_widget_no_matches_outline: Option<String>,
}
fn try_parse_color(color: &str) -> Result<Hsla> {
Ok(Rgba::try_from(color)?.into())
}
pub struct VsCodeThemeConverter {
theme: VsCodeTheme,
theme_metadata: ThemeMetadata,
}
impl VsCodeThemeConverter {
pub fn new(theme: VsCodeTheme, theme_metadata: ThemeMetadata) -> Self {
Self {
theme,
theme_metadata,
}
}
pub fn convert(self) -> Result<UserTheme> {
let appearance = self.theme_metadata.appearance.into();
let vscode_colors = &self.theme.colors;
let theme_colors_refinements = ThemeColorsRefinement {
border: vscode_colors
.panel_border
.as_ref()
.traverse(|color| try_parse_color(&color))?,
border_variant: vscode_colors
.panel_border
.as_ref()
.traverse(|color| try_parse_color(&color))?,
border_focused: vscode_colors
.panel_border
.as_ref()
.traverse(|color| try_parse_color(&color))?,
border_disabled: vscode_colors
.panel_border
.as_ref()
.traverse(|color| try_parse_color(&color))?,
border_selected: vscode_colors
.panel_border
.as_ref()
.traverse(|color| try_parse_color(&color))?,
border_transparent: vscode_colors
.panel_border
.as_ref()
.traverse(|color| try_parse_color(&color))?,
elevated_surface_background: vscode_colors
.panel_background
.as_ref()
.traverse(|color| try_parse_color(&color))?,
surface_background: vscode_colors
.panel_background
.as_ref()
.traverse(|color| try_parse_color(&color))?,
background: vscode_colors
.editor_background
.as_ref()
.traverse(|color| try_parse_color(&color))?,
element_background: vscode_colors
.button_background
.as_ref()
.traverse(|color| try_parse_color(&color))?,
text: vscode_colors
.foreground
.as_ref()
.traverse(|color| try_parse_color(&color))?,
tab_active_background: vscode_colors
.tab_active_background
.as_ref()
.traverse(|color| try_parse_color(&color))?,
tab_inactive_background: vscode_colors
.tab_inactive_background
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_background: vscode_colors
.terminal_background
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_black: vscode_colors
.terminal_ansi_bright_black
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_red: vscode_colors
.terminal_ansi_bright_red
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_green: vscode_colors
.terminal_ansi_bright_green
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_yellow: vscode_colors
.terminal_ansi_bright_yellow
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_blue: vscode_colors
.terminal_ansi_bright_blue
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_magenta: vscode_colors
.terminal_ansi_bright_magenta
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_cyan: vscode_colors
.terminal_ansi_bright_cyan
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_bright_white: vscode_colors
.terminal_ansi_bright_white
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_black: vscode_colors
.terminal_ansi_black
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_red: vscode_colors
.terminal_ansi_red
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_green: vscode_colors
.terminal_ansi_green
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_yellow: vscode_colors
.terminal_ansi_yellow
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_blue: vscode_colors
.terminal_ansi_blue
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_magenta: vscode_colors
.terminal_ansi_magenta
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_cyan: vscode_colors
.terminal_ansi_cyan
.as_ref()
.traverse(|color| try_parse_color(&color))?,
terminal_ansi_white: vscode_colors
.terminal_ansi_white
.as_ref()
.traverse(|color| try_parse_color(&color))?,
..Default::default()
};
Ok(UserTheme {
name: self.theme_metadata.name.into(),
appearance,
styles: UserThemeStylesRefinement {
colors: theme_colors_refinements,
},
})
}
}

View file

@ -128,7 +128,7 @@ impl<V: 'static> Checkbox<V> {
// click area for the checkbox.
.size_5()
// Because we've enlarged the click area, we need to create a
// `group` to pass down interaction events to the checkbox.
// `group` to pass down interactivity events to the checkbox.
.group(group_id.clone())
.child(
div()
@ -148,7 +148,7 @@ impl<V: 'static> Checkbox<V> {
.bg(bg_color)
.border()
.border_color(border_color)
// We only want the interaction states to fire when we
// We only want the interactivity states to fire when we
// are in a checkbox that isn't disabled.
.when(!self.disabled, |this| {
// Here instead of `hover()` we use `group_hover()`

View file

@ -309,6 +309,10 @@ impl ListEntry {
.group("")
.bg(cx.theme().colors().surface_background)
// TODO: Add focus state
// .when(self.state == InteractionState::Focused, |this| {
// this.border()
// .border_color(cx.theme().colors().border_focused)
// })
.child(
sized_item
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())

View file

@ -127,7 +127,7 @@ impl Tab {
div()
.id(self.id.clone())
.on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone()))
.drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().element_drop_target))
.drag_over::<TabDragState>(|d| d.bg(cx.theme().colors().drop_target_background))
.on_drop(|_view, state: View<TabDragState>, cx| {
eprintln!("{:?}", state.read(cx));
})

View file

@ -46,12 +46,12 @@ pub enum GitStatus {
impl GitStatus {
pub fn hsla(&self, cx: &WindowContext) -> Hsla {
match self {
Self::None => cx.theme().styles.system.transparent,
Self::Created => cx.theme().styles.git.created,
Self::Modified => cx.theme().styles.git.modified,
Self::Deleted => cx.theme().styles.git.deleted,
Self::Conflict => cx.theme().styles.git.conflict,
Self::Renamed => cx.theme().styles.git.renamed,
Self::None => cx.theme().system().transparent,
Self::Created => cx.theme().status().created,
Self::Modified => cx.theme().status().modified,
Self::Deleted => cx.theme().status().deleted,
Self::Conflict => cx.theme().status().conflict,
Self::Renamed => cx.theme().status().renamed,
}
}
}

View file

@ -156,7 +156,7 @@ impl Buffer {
fn render_row<V: 'static>(row: BufferRow, cx: &WindowContext) -> impl Component<V> {
let line_background = if row.current {
cx.theme().colors().editor_active_line
cx.theme().colors().editor_active_line_background
} else {
cx.theme().styles.system.transparent
};

Some files were not shown because too many files have changed in this diff Show more