mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-08 19:51:19 +03:00
Extract most colors in codebase into theme file. switch to dark
This commit is contained in:
parent
c306ac007c
commit
372d2ccb6d
@ -20,6 +20,7 @@ pub struct Label {
|
||||
family_id: FamilyId,
|
||||
font_properties: Properties,
|
||||
font_size: f32,
|
||||
default_color: ColorU,
|
||||
highlights: Option<Highlights>,
|
||||
}
|
||||
|
||||
@ -36,10 +37,16 @@ impl Label {
|
||||
family_id,
|
||||
font_properties: Properties::new(),
|
||||
font_size,
|
||||
default_color: ColorU::black(),
|
||||
highlights: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_default_color(mut self, color: ColorU) -> Self {
|
||||
self.default_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_highlights(
|
||||
mut self,
|
||||
color: ColorU,
|
||||
@ -69,7 +76,7 @@ impl Label {
|
||||
|
||||
for (char_ix, c) in self.text.char_indices() {
|
||||
let mut font_id = font_id;
|
||||
let mut color = ColorU::black();
|
||||
let mut color = self.default_color;
|
||||
if let Some(highlight_ix) = highlight_indices.peek() {
|
||||
if char_ix == *highlight_ix {
|
||||
font_id = highlight_font_id;
|
||||
@ -97,7 +104,7 @@ impl Label {
|
||||
|
||||
runs
|
||||
} else {
|
||||
smallvec![(self.text.len(), font_id, ColorU::black())]
|
||||
smallvec![(self.text.len(), font_id, self.default_color)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
zed/assets/themes/dark.toml
Normal file
38
zed/assets/themes/dark.toml
Normal file
@ -0,0 +1,38 @@
|
||||
[ui]
|
||||
tab_background = 0x131415
|
||||
tab_background_active = 0x1c1d1e
|
||||
tab_text = 0x5a5a5b
|
||||
tab_text_active = 0xffffff
|
||||
tab_border = 0x000000
|
||||
tab_icon_close = 0x383839
|
||||
tab_icon_dirty = 0x556de8
|
||||
tab_icon_conflict = 0xe45349
|
||||
modal_background = 0x3a3b3c
|
||||
modal_match_background = 0x424344
|
||||
modal_match_background_active = 0x094771
|
||||
modal_match_border = 0x000000
|
||||
modal_match_text = 0xcccccc
|
||||
modal_match_text_highlight = 0x18a3ff
|
||||
|
||||
|
||||
[editor]
|
||||
background = 0x1c1d1e
|
||||
gutter_background = 0x1c1d1e
|
||||
line_number = 0x5a5a5b
|
||||
line_number_active = 0xffffff
|
||||
default_text = 0xd4d4d4
|
||||
replicas = [
|
||||
{ selection = 0x264f78, cursor = 0xffffff },
|
||||
{ selection = 0x504f31, cursor = 0xfcf154 },
|
||||
]
|
||||
|
||||
[syntax]
|
||||
keyword = 0xc586c0
|
||||
function = 0xdcdcaa
|
||||
string = 0xcb8f77
|
||||
type = 0x4ec9b0
|
||||
number = 0xb5cea8
|
||||
comment = 0x6a9955
|
||||
property = 0x4e94ce
|
||||
variant = 0x4fc1ff
|
||||
constant = 0x9cdcfe
|
@ -1,13 +0,0 @@
|
||||
[ui]
|
||||
background = 0xffffff
|
||||
line_numbers = 0x237791
|
||||
text = 0x0d0d0d
|
||||
|
||||
[syntax]
|
||||
keyword = 0xaf00db
|
||||
function = 0x795e26
|
||||
string = 0xa31515
|
||||
type = 0x267599
|
||||
number = 0x0d885b
|
||||
comment = 0x048204
|
||||
property = 0x001080
|
@ -15,7 +15,27 @@
|
||||
(function_item name: (identifier) @function.definition)
|
||||
(function_signature_item name: (identifier) @function.definition)
|
||||
|
||||
; Identifier conventions
|
||||
|
||||
; Assume uppercase names are enum constructors
|
||||
((identifier) @variant
|
||||
(#match? @variant "^[A-Z]"))
|
||||
|
||||
; Assume that uppercase names in paths are types
|
||||
((scoped_identifier
|
||||
path: (identifier) @type)
|
||||
(#match? @type "^[A-Z]"))
|
||||
((scoped_identifier
|
||||
path: (scoped_identifier
|
||||
name: (identifier) @type))
|
||||
(#match? @type "^[A-Z]"))
|
||||
|
||||
; Assume all-caps names are constants
|
||||
((identifier) @constant
|
||||
(#match? @constant "^[A-Z][A-Z\\d_]+$'"))
|
||||
|
||||
[
|
||||
"as"
|
||||
"async"
|
||||
"break"
|
||||
"const"
|
||||
|
@ -2353,6 +2353,7 @@ impl Snapshot {
|
||||
viewport_height: f32,
|
||||
font_cache: &FontCache,
|
||||
layout_cache: &TextLayoutCache,
|
||||
theme: &Theme,
|
||||
) -> Result<Vec<Option<text_layout::Line>>> {
|
||||
let font_id = font_cache.select_font(self.font_family, &FontProperties::new())?;
|
||||
|
||||
@ -2378,7 +2379,7 @@ impl Snapshot {
|
||||
layouts.push(Some(layout_cache.layout_str(
|
||||
&line_number,
|
||||
self.font_size,
|
||||
&[(line_number.len(), font_id, ColorU::black())],
|
||||
&[(line_number.len(), font_id, theme.editor.line_number.0)],
|
||||
)));
|
||||
}
|
||||
}
|
||||
@ -2785,12 +2786,13 @@ mod tests {
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6), cx));
|
||||
|
||||
let settings = settings::channel(&font_cache).unwrap().1;
|
||||
let (_, editor) = cx.add_window(|cx| Editor::for_buffer(buffer.clone(), settings, cx));
|
||||
let (_, editor) =
|
||||
cx.add_window(|cx| Editor::for_buffer(buffer.clone(), settings.clone(), cx));
|
||||
|
||||
let layouts = editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.snapshot(cx)
|
||||
.layout_line_numbers(1000.0, &font_cache, &layout_cache)
|
||||
.layout_line_numbers(1000.0, &font_cache, &layout_cache, &settings.borrow().theme)
|
||||
.unwrap()
|
||||
});
|
||||
assert_eq!(layouts.len(), 6);
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::time::ReplicaId;
|
||||
|
||||
use super::{DisplayPoint, Editor, SelectAction, Snapshot};
|
||||
use crate::time::ReplicaId;
|
||||
use gpui::{
|
||||
color::ColorU,
|
||||
geometry::{
|
||||
@ -184,12 +183,14 @@ impl EditorElement {
|
||||
}
|
||||
|
||||
fn paint_gutter(&mut self, rect: RectF, layout: &LayoutState, cx: &mut PaintContext) {
|
||||
let settings = self.view(cx.app).settings.borrow();
|
||||
let theme = &settings.theme;
|
||||
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
|
||||
|
||||
cx.scene.push_layer(Some(rect));
|
||||
cx.scene.push_quad(Quad {
|
||||
bounds: rect,
|
||||
background: Some(ColorU::white()),
|
||||
background: Some(theme.editor.gutter_background.0),
|
||||
border: Border::new(0., ColorU::transparent_black()),
|
||||
corner_radius: 0.,
|
||||
});
|
||||
@ -214,6 +215,8 @@ impl EditorElement {
|
||||
|
||||
fn paint_text(&mut self, bounds: RectF, layout: &LayoutState, cx: &mut PaintContext) {
|
||||
let view = self.view(cx.app);
|
||||
let settings = self.view(cx.app).settings.borrow();
|
||||
let theme = &settings.theme.editor;
|
||||
let scroll_position = layout.snapshot.scroll_position();
|
||||
let start_row = scroll_position.y() as u32;
|
||||
let scroll_top = scroll_position.y() * layout.line_height;
|
||||
@ -224,26 +227,19 @@ impl EditorElement {
|
||||
cx.scene.push_layer(Some(bounds));
|
||||
cx.scene.push_quad(Quad {
|
||||
bounds,
|
||||
background: Some(ColorU::white()),
|
||||
background: Some(theme.background.0),
|
||||
border: Border::new(0., ColorU::transparent_black()),
|
||||
corner_radius: 0.,
|
||||
});
|
||||
|
||||
// Draw selections
|
||||
let corner_radius = 2.5;
|
||||
let colors = [
|
||||
(ColorU::from_u32(0xa3d6ffff), ColorU::from_u32(0x000000ff)),
|
||||
(ColorU::from_u32(0xffaf87ff), ColorU::from_u32(0xff8e72ff)),
|
||||
(ColorU::from_u32(0x86eaccff), ColorU::from_u32(0x377771ff)),
|
||||
(ColorU::from_u32(0xb8b8ffff), ColorU::from_u32(0x9381ffff)),
|
||||
(ColorU::from_u32(0xf5cce8ff), ColorU::from_u32(0x4a2040ff)),
|
||||
];
|
||||
let mut cursors = SmallVec::<[Cursor; 32]>::new();
|
||||
|
||||
let content_origin = bounds.origin() + layout.text_offset;
|
||||
|
||||
for (replica_id, selections) in &layout.selections {
|
||||
let (selection_color, cursor_color) = colors[*replica_id as usize % colors.len()];
|
||||
let replica_theme = theme.replicas[*replica_id as usize % theme.replicas.len()];
|
||||
|
||||
for selection in selections {
|
||||
if selection.start != selection.end {
|
||||
@ -257,7 +253,7 @@ impl EditorElement {
|
||||
};
|
||||
|
||||
let selection = Selection {
|
||||
color: selection_color,
|
||||
color: replica_theme.selection.0,
|
||||
line_height: layout.line_height,
|
||||
start_y: content_origin.y() + row_range.start as f32 * layout.line_height
|
||||
- scroll_top,
|
||||
@ -300,7 +296,7 @@ impl EditorElement {
|
||||
- scroll_left;
|
||||
let y = selection.end.row() as f32 * layout.line_height - scroll_top;
|
||||
cursors.push(Cursor {
|
||||
color: cursor_color,
|
||||
color: replica_theme.cursor.0,
|
||||
origin: content_origin + vec2f(x, y),
|
||||
line_height: layout.line_height,
|
||||
});
|
||||
@ -392,7 +388,19 @@ impl Element for EditorElement {
|
||||
});
|
||||
|
||||
let line_number_layouts = if snapshot.gutter_visible {
|
||||
match snapshot.layout_line_numbers(size.y(), cx.font_cache, cx.text_layout_cache) {
|
||||
let settings = self
|
||||
.view
|
||||
.upgrade(cx.app)
|
||||
.unwrap()
|
||||
.read(cx.app)
|
||||
.settings
|
||||
.borrow();
|
||||
match snapshot.layout_line_numbers(
|
||||
size.y(),
|
||||
cx.font_cache,
|
||||
cx.text_layout_cache,
|
||||
&settings.theme,
|
||||
) {
|
||||
Err(error) => {
|
||||
log::error!("error laying out line numbers: {}", error);
|
||||
return (size, None);
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
worktree::{match_paths, PathMatch, Worktree},
|
||||
};
|
||||
use gpui::{
|
||||
color::{ColorF, ColorU},
|
||||
color::ColorF,
|
||||
elements::*,
|
||||
fonts::{Properties, Weight},
|
||||
geometry::vector::vec2f,
|
||||
@ -69,6 +69,8 @@ impl View for FileFinder {
|
||||
}
|
||||
|
||||
fn render(&self, _: &AppContext) -> ElementBox {
|
||||
let settings = self.settings.borrow();
|
||||
|
||||
Align::new(
|
||||
ConstrainedBox::new(
|
||||
Container::new(
|
||||
@ -80,8 +82,8 @@ impl View for FileFinder {
|
||||
.with_margin_top(12.0)
|
||||
.with_uniform_padding(6.0)
|
||||
.with_corner_radius(6.0)
|
||||
.with_background_color(ColorU::from_u32(0xf2f2f2ff))
|
||||
.with_shadow(vec2f(0., 4.), 12., ColorF::new(0.0, 0.0, 0.0, 0.25).to_u8())
|
||||
.with_background_color(settings.theme.ui.modal_background)
|
||||
.with_shadow(vec2f(0., 4.), 12., ColorF::new(0.0, 0.0, 0.0, 0.5).to_u8())
|
||||
.boxed(),
|
||||
)
|
||||
.with_max_width(600.0)
|
||||
@ -113,6 +115,7 @@ impl FileFinder {
|
||||
settings.ui_font_family,
|
||||
settings.ui_font_size,
|
||||
)
|
||||
.with_default_color(settings.theme.editor.default_text.0)
|
||||
.boxed(),
|
||||
)
|
||||
.with_margin_top(6.0)
|
||||
@ -136,8 +139,6 @@ impl FileFinder {
|
||||
);
|
||||
|
||||
Container::new(list.boxed())
|
||||
.with_background_color(ColorU::from_u32(0xf7f7f7ff))
|
||||
.with_border(Border::all(1.0, ColorU::from_u32(0xdbdbdcff)))
|
||||
.with_margin_top(6.0)
|
||||
.named("matches")
|
||||
}
|
||||
@ -148,11 +149,12 @@ impl FileFinder {
|
||||
index: usize,
|
||||
cx: &AppContext,
|
||||
) -> Option<ElementBox> {
|
||||
let settings = self.settings.borrow();
|
||||
let theme = &settings.theme.ui;
|
||||
self.labels_for_match(path_match, cx).map(
|
||||
|(file_name, file_name_positions, full_path, full_path_positions)| {
|
||||
let settings = self.settings.borrow();
|
||||
let highlight_color = ColorU::from_u32(0x304ee2ff);
|
||||
let bold = *Properties::new().weight(Weight::BOLD);
|
||||
let selected_index = self.selected_index();
|
||||
let mut container = Container::new(
|
||||
Flex::row()
|
||||
.with_child(
|
||||
@ -177,7 +179,12 @@ impl FileFinder {
|
||||
settings.ui_font_family,
|
||||
settings.ui_font_size,
|
||||
)
|
||||
.with_highlights(highlight_color, bold, file_name_positions)
|
||||
.with_default_color(theme.modal_match_text.0)
|
||||
.with_highlights(
|
||||
theme.modal_match_text_highlight.0,
|
||||
bold,
|
||||
file_name_positions,
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_child(
|
||||
@ -186,7 +193,12 @@ impl FileFinder {
|
||||
settings.ui_font_family,
|
||||
settings.ui_font_size,
|
||||
)
|
||||
.with_highlights(highlight_color, bold, full_path_positions)
|
||||
.with_default_color(theme.modal_match_text.0)
|
||||
.with_highlights(
|
||||
theme.modal_match_text_highlight.0,
|
||||
bold,
|
||||
full_path_positions,
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
@ -195,16 +207,16 @@ impl FileFinder {
|
||||
)
|
||||
.boxed(),
|
||||
)
|
||||
.with_uniform_padding(6.0);
|
||||
.with_uniform_padding(6.0)
|
||||
.with_background_color(if index == selected_index {
|
||||
theme.modal_match_background_active.0
|
||||
} else {
|
||||
theme.modal_match_background.0
|
||||
});
|
||||
|
||||
let selected_index = self.selected_index();
|
||||
if index == selected_index || index < self.matches.len() - 1 {
|
||||
container =
|
||||
container.with_border(Border::bottom(1.0, ColorU::from_u32(0xdbdbdcff)));
|
||||
}
|
||||
|
||||
if index == selected_index {
|
||||
container = container.with_background_color(ColorU::from_u32(0xdbdbdcff));
|
||||
container.with_border(Border::bottom(1.0, theme.modal_match_border));
|
||||
}
|
||||
|
||||
let entry = (path_match.tree_id, path_match.path.clone());
|
||||
|
@ -7,7 +7,12 @@ use gpui::{
|
||||
};
|
||||
use postage::watch;
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
const DEFAULT_STYLE_ID: StyleId = StyleId(u32::MAX);
|
||||
|
||||
@ -23,12 +28,50 @@ pub struct Settings {
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Theme {
|
||||
pub background_color: ColorU,
|
||||
pub line_number_color: ColorU,
|
||||
pub default_text_color: ColorU,
|
||||
syntax_styles: Vec<(String, ColorU, FontProperties)>,
|
||||
pub ui: UiTheme,
|
||||
pub editor: EditorTheme,
|
||||
syntax: Vec<(String, ColorU, FontProperties)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct UiTheme {
|
||||
pub tab_background: Color,
|
||||
pub tab_background_active: Color,
|
||||
pub tab_text: Color,
|
||||
pub tab_text_active: Color,
|
||||
pub tab_border: Color,
|
||||
pub tab_icon_close: Color,
|
||||
pub tab_icon_dirty: Color,
|
||||
pub tab_icon_conflict: Color,
|
||||
pub modal_background: Color,
|
||||
pub modal_match_background: Color,
|
||||
pub modal_match_background_active: Color,
|
||||
pub modal_match_border: Color,
|
||||
pub modal_match_text: Color,
|
||||
pub modal_match_text_highlight: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct EditorTheme {
|
||||
pub background: Color,
|
||||
pub gutter_background: Color,
|
||||
pub line_number: Color,
|
||||
pub line_number_active: Color,
|
||||
pub default_text: Color,
|
||||
pub replicas: Vec<ReplicaTheme>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Deserialize)]
|
||||
pub struct ReplicaTheme {
|
||||
pub cursor: Color,
|
||||
pub selection: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct Color(pub ColorU);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ThemeMap(Arc<[StyleId]>);
|
||||
|
||||
@ -44,7 +87,7 @@ impl Settings {
|
||||
ui_font_family: font_cache.load_family(&["SF Pro", "Helvetica"])?,
|
||||
ui_font_size: 12.0,
|
||||
theme: Arc::new(
|
||||
Theme::parse(Assets::get("themes/light.toml").unwrap())
|
||||
Theme::parse(Assets::get("themes/dark.toml").unwrap())
|
||||
.expect("Failed to parse built-in theme"),
|
||||
),
|
||||
})
|
||||
@ -61,17 +104,19 @@ impl Theme {
|
||||
#[derive(Deserialize)]
|
||||
struct ThemeToml {
|
||||
#[serde(default)]
|
||||
syntax: HashMap<String, StyleToml>,
|
||||
ui: UiTheme,
|
||||
#[serde(default)]
|
||||
ui: HashMap<String, u32>,
|
||||
editor: EditorTheme,
|
||||
#[serde(default)]
|
||||
syntax: HashMap<String, StyleToml>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum StyleToml {
|
||||
Color(u32),
|
||||
Color(Color),
|
||||
Full {
|
||||
color: Option<u32>,
|
||||
color: Option<Color>,
|
||||
weight: Option<toml::Value>,
|
||||
#[serde(default)]
|
||||
italic: bool,
|
||||
@ -81,7 +126,7 @@ impl Theme {
|
||||
let theme_toml: ThemeToml =
|
||||
toml::from_slice(source.as_ref()).context("failed to parse theme TOML")?;
|
||||
|
||||
let mut syntax_styles = Vec::<(String, ColorU, FontProperties)>::new();
|
||||
let mut syntax = Vec::<(String, ColorU, FontProperties)>::new();
|
||||
for (key, style) in theme_toml.syntax {
|
||||
let (color, weight, italic) = match style {
|
||||
StyleToml::Color(color) => (color, None, false),
|
||||
@ -89,55 +134,37 @@ impl Theme {
|
||||
color,
|
||||
weight,
|
||||
italic,
|
||||
} => (color.unwrap_or(0), weight, italic),
|
||||
} => (color.unwrap_or(Color::default()), weight, italic),
|
||||
};
|
||||
match syntax_styles.binary_search_by_key(&&key, |e| &e.0) {
|
||||
match syntax.binary_search_by_key(&&key, |e| &e.0) {
|
||||
Ok(i) | Err(i) => {
|
||||
let mut properties = FontProperties::new();
|
||||
properties.weight = deserialize_weight(weight)?;
|
||||
if italic {
|
||||
properties.style = FontStyle::Italic;
|
||||
}
|
||||
syntax_styles.insert(i, (key, deserialize_color(color), properties));
|
||||
syntax.insert(i, (key, color.0, properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let background_color = theme_toml
|
||||
.ui
|
||||
.get("background")
|
||||
.copied()
|
||||
.map_or(ColorU::from_u32(0xffffffff), deserialize_color);
|
||||
let line_number_color = theme_toml
|
||||
.ui
|
||||
.get("line_numbers")
|
||||
.copied()
|
||||
.map_or(ColorU::black(), deserialize_color);
|
||||
let default_text_color = theme_toml
|
||||
.ui
|
||||
.get("text")
|
||||
.copied()
|
||||
.map_or(ColorU::black(), deserialize_color);
|
||||
|
||||
Ok(Theme {
|
||||
background_color,
|
||||
line_number_color,
|
||||
default_text_color,
|
||||
syntax_styles,
|
||||
ui: theme_toml.ui,
|
||||
editor: theme_toml.editor,
|
||||
syntax,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn syntax_style(&self, id: StyleId) -> (ColorU, FontProperties) {
|
||||
self.syntax_styles
|
||||
.get(id.0 as usize)
|
||||
.map_or((self.default_text_color, FontProperties::new()), |entry| {
|
||||
(entry.1, entry.2)
|
||||
})
|
||||
self.syntax.get(id.0 as usize).map_or(
|
||||
(self.editor.default_text.0, FontProperties::new()),
|
||||
|entry| (entry.1, entry.2),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn syntax_style_name(&self, id: StyleId) -> Option<&str> {
|
||||
self.syntax_styles.get(id.0 as usize).map(|e| e.0.as_str())
|
||||
self.syntax.get(id.0 as usize).map(|e| e.0.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +178,7 @@ impl ThemeMap {
|
||||
.iter()
|
||||
.map(|capture_name| {
|
||||
theme
|
||||
.syntax_styles
|
||||
.syntax
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, (key, _, _))| {
|
||||
@ -193,16 +220,53 @@ impl Default for StyleId {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Color {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let rgba_value = u32::deserialize(deserializer)?;
|
||||
Ok(Self(ColorU::from_u32((rgba_value << 8) + 0xFF)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ColorU> for Color {
|
||||
fn into(self) -> ColorU {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Color {
|
||||
type Target = ColorU;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Color {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Color {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ColorU> for Color {
|
||||
fn eq(&self, other: &ColorU) -> bool {
|
||||
self.0.eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channel(
|
||||
font_cache: &FontCache,
|
||||
) -> Result<(watch::Sender<Settings>, watch::Receiver<Settings>)> {
|
||||
Ok(watch::channel_with(Settings::new(font_cache)?))
|
||||
}
|
||||
|
||||
fn deserialize_color(color: u32) -> ColorU {
|
||||
ColorU::from_u32((color << 8) + 0xFF)
|
||||
}
|
||||
|
||||
fn deserialize_weight(weight: Option<toml::Value>) -> Result<FontWeight> {
|
||||
match &weight {
|
||||
None => return Ok(FontWeight::NORMAL),
|
||||
@ -228,8 +292,11 @@ mod tests {
|
||||
let theme = Theme::parse(
|
||||
r#"
|
||||
[ui]
|
||||
tab_background_active = 0x100000
|
||||
|
||||
[editor]
|
||||
background = 0x00ed00
|
||||
line_numbers = 0xdddddd
|
||||
line_number = 0xdddddd
|
||||
|
||||
[syntax]
|
||||
"beta.two" = 0xAABBCC
|
||||
@ -239,24 +306,25 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(theme.background_color, ColorU::from_u32(0x00ED00FF));
|
||||
assert_eq!(theme.line_number_color, ColorU::from_u32(0xddddddff));
|
||||
assert_eq!(theme.ui.tab_background_active, ColorU::from_u32(0x100000ff));
|
||||
assert_eq!(theme.editor.background, ColorU::from_u32(0x00ed00ff));
|
||||
assert_eq!(theme.editor.line_number, ColorU::from_u32(0xddddddff));
|
||||
assert_eq!(
|
||||
theme.syntax_styles,
|
||||
theme.syntax,
|
||||
&[
|
||||
(
|
||||
"alpha.one".to_string(),
|
||||
ColorU::from_u32(0x112233FF),
|
||||
ColorU::from_u32(0x112233ff),
|
||||
*FontProperties::new().weight(FontWeight::BOLD)
|
||||
),
|
||||
(
|
||||
"beta.two".to_string(),
|
||||
ColorU::from_u32(0xAABBCCFF),
|
||||
ColorU::from_u32(0xaabbccff),
|
||||
*FontProperties::new().weight(FontWeight::NORMAL)
|
||||
),
|
||||
(
|
||||
"gamma.three".to_string(),
|
||||
ColorU::from_u32(0x000000FF),
|
||||
ColorU::from_u32(0x00000000),
|
||||
*FontProperties::new()
|
||||
.weight(FontWeight::LIGHT)
|
||||
.style(FontStyle::Italic),
|
||||
@ -273,10 +341,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_theme_map() {
|
||||
let theme = Theme {
|
||||
default_text_color: Default::default(),
|
||||
background_color: ColorU::default(),
|
||||
line_number_color: ColorU::default(),
|
||||
syntax_styles: [
|
||||
ui: Default::default(),
|
||||
editor: Default::default(),
|
||||
syntax: [
|
||||
("function", ColorU::from_u32(0x100000ff)),
|
||||
("function.method", ColorU::from_u32(0x200000ff)),
|
||||
("function.async", ColorU::from_u32(0x300000ff)),
|
||||
|
@ -12,9 +12,9 @@ use crate::{
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{
|
||||
color::rgbu, elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext,
|
||||
ClipboardItem, Entity, ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, Task,
|
||||
View, ViewContext, ViewHandle, WeakModelHandle,
|
||||
elements::*, json::to_string_pretty, keymap::Binding, AnyViewHandle, AppContext, ClipboardItem,
|
||||
Entity, ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, Task, View,
|
||||
ViewContext, ViewHandle, WeakModelHandle,
|
||||
};
|
||||
use log::error;
|
||||
pub use pane::*;
|
||||
@ -880,14 +880,14 @@ impl View for Workspace {
|
||||
}
|
||||
|
||||
fn render(&self, _: &AppContext) -> ElementBox {
|
||||
let settings = self.settings.borrow();
|
||||
Container::new(
|
||||
// self.center.render(bump)
|
||||
Stack::new()
|
||||
.with_child(self.center.render())
|
||||
.with_children(self.modal.as_ref().map(|m| ChildView::new(m.id()).boxed()))
|
||||
.boxed(),
|
||||
)
|
||||
.with_background_color(rgbu(0xea, 0xea, 0xeb))
|
||||
.with_background_color(settings.theme.editor.background)
|
||||
.named("workspace")
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::{ItemViewHandle, SplitDirection};
|
||||
use crate::settings::Settings;
|
||||
use crate::settings::{Settings, UiTheme};
|
||||
use gpui::{
|
||||
color::ColorU,
|
||||
elements::*,
|
||||
@ -180,7 +180,7 @@ impl Pane {
|
||||
|
||||
fn render_tabs(&self, cx: &AppContext) -> ElementBox {
|
||||
let settings = self.settings.borrow();
|
||||
let border_color = ColorU::from_u32(0xdbdbdcff);
|
||||
let theme = &settings.theme.ui;
|
||||
let line_height = cx.font_cache().line_height(
|
||||
cx.font_cache().default_font(settings.ui_font_family),
|
||||
settings.ui_font_size,
|
||||
@ -189,6 +189,8 @@ impl Pane {
|
||||
let mut row = Flex::row();
|
||||
let last_item_ix = self.items.len() - 1;
|
||||
for (ix, item) in self.items.iter().enumerate() {
|
||||
let is_active = ix == self.active_item;
|
||||
|
||||
enum Tab {}
|
||||
|
||||
row.add_child(
|
||||
@ -197,7 +199,7 @@ impl Pane {
|
||||
MouseEventHandler::new::<Tab, _>(item.id(), cx, |mouse_state| {
|
||||
let title = item.title(cx);
|
||||
|
||||
let mut border = Border::new(1.0, border_color);
|
||||
let mut border = Border::new(1.0, theme.tab_border.0);
|
||||
border.left = ix > 0;
|
||||
border.right = ix == last_item_ix;
|
||||
border.bottom = ix != self.active_item;
|
||||
@ -211,6 +213,11 @@ impl Pane {
|
||||
settings.ui_font_family,
|
||||
settings.ui_font_size,
|
||||
)
|
||||
.with_default_color(if is_active {
|
||||
theme.tab_text_active.0
|
||||
} else {
|
||||
theme.tab_text.0
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed(),
|
||||
@ -222,6 +229,7 @@ impl Pane {
|
||||
mouse_state.hovered,
|
||||
item.is_dirty(cx),
|
||||
item.has_conflict(cx),
|
||||
theme,
|
||||
cx,
|
||||
))
|
||||
.right()
|
||||
@ -232,13 +240,12 @@ impl Pane {
|
||||
.with_horizontal_padding(10.)
|
||||
.with_border(border);
|
||||
|
||||
if ix == self.active_item {
|
||||
if is_active {
|
||||
container = container
|
||||
.with_background_color(ColorU::white())
|
||||
.with_background_color(theme.tab_background_active)
|
||||
.with_padding_bottom(border.width);
|
||||
} else {
|
||||
container =
|
||||
container.with_background_color(ColorU::from_u32(0xeaeaebff));
|
||||
container = container.with_background_color(theme.tab_background);
|
||||
}
|
||||
|
||||
ConstrainedBox::new(
|
||||
@ -264,7 +271,7 @@ impl Pane {
|
||||
row.add_child(
|
||||
ConstrainedBox::new(
|
||||
Container::new(Empty::new().boxed())
|
||||
.with_border(Border::bottom(1.0, border_color))
|
||||
.with_border(Border::bottom(1.0, theme.tab_border))
|
||||
.boxed(),
|
||||
)
|
||||
.with_min_width(20.)
|
||||
@ -275,7 +282,7 @@ impl Pane {
|
||||
Expanded::new(
|
||||
0.0,
|
||||
Container::new(Empty::new().boxed())
|
||||
.with_border(Border::bottom(1.0, border_color))
|
||||
.with_border(Border::bottom(1.0, theme.tab_border))
|
||||
.boxed(),
|
||||
)
|
||||
.named("filler"),
|
||||
@ -292,25 +299,25 @@ impl Pane {
|
||||
tab_hovered: bool,
|
||||
is_dirty: bool,
|
||||
has_conflict: bool,
|
||||
theme: &UiTheme,
|
||||
cx: &AppContext,
|
||||
) -> ElementBox {
|
||||
enum TabCloseButton {}
|
||||
|
||||
let dirty_color = ColorU::from_u32(0x556de8ff);
|
||||
let conflict_color = ColorU::from_u32(0xe45349ff);
|
||||
let mut clicked_color = dirty_color;
|
||||
let mut clicked_color = theme.tab_icon_dirty;
|
||||
clicked_color.a = 180;
|
||||
|
||||
let current_color = if has_conflict {
|
||||
Some(conflict_color)
|
||||
Some(theme.tab_icon_conflict)
|
||||
} else if is_dirty {
|
||||
Some(dirty_color)
|
||||
Some(theme.tab_icon_dirty)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let icon = if tab_hovered {
|
||||
let mut icon = Svg::new("icons/x.svg");
|
||||
let close_color = current_color.unwrap_or(theme.tab_icon_close).0;
|
||||
let icon = Svg::new("icons/x.svg").with_color(close_color);
|
||||
|
||||
MouseEventHandler::new::<TabCloseButton, _>(item_id, cx, |mouse_state| {
|
||||
if mouse_state.hovered {
|
||||
@ -318,14 +325,11 @@ impl Pane {
|
||||
.with_background_color(if mouse_state.clicked {
|
||||
clicked_color
|
||||
} else {
|
||||
dirty_color
|
||||
theme.tab_icon_dirty
|
||||
})
|
||||
.with_corner_radius(close_icon_size / 2.)
|
||||
.boxed()
|
||||
} else {
|
||||
if let Some(current_color) = current_color {
|
||||
icon = icon.with_color(current_color);
|
||||
}
|
||||
icon.boxed()
|
||||
}
|
||||
})
|
||||
@ -339,7 +343,7 @@ impl Pane {
|
||||
let square = RectF::new(bounds.origin(), vec2f(diameter, diameter));
|
||||
cx.scene.push_quad(Quad {
|
||||
bounds: square,
|
||||
background: Some(current_color),
|
||||
background: Some(current_color.0),
|
||||
border: Default::default(),
|
||||
corner_radius: diameter / 2.,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user