mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 20:32:22 +03:00
Merge branch 'main' into disclosable-component
This commit is contained in:
commit
707ca34f19
7
.github/workflows/publish_collab_image.yml
vendored
7
.github/workflows/publish_collab_image.yml
vendored
@ -11,7 +11,7 @@ env:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish collab server image
|
||||
name: Publish collab server image
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- deploy
|
||||
@ -22,6 +22,9 @@ jobs:
|
||||
- name: Sign into DigitalOcean docker registry
|
||||
run: doctl registry login
|
||||
|
||||
- name: Prune Docker system
|
||||
run: docker system prune
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@ -41,6 +44,6 @@ jobs:
|
||||
|
||||
- name: Build docker image
|
||||
run: docker build . --tag registry.digitalocean.com/zed/collab:v${COLLAB_VERSION}
|
||||
|
||||
|
||||
- name: Publish docker image
|
||||
run: docker push registry.digitalocean.com/zed/collab:v${COLLAB_VERSION}
|
||||
|
170
Cargo.lock
generated
170
Cargo.lock
generated
@ -1312,12 +1312,6 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
|
||||
[[package]]
|
||||
name = "claxon"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
|
||||
|
||||
[[package]]
|
||||
name = "cli"
|
||||
version = "0.1.0"
|
||||
@ -1415,7 +1409,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.17.0"
|
||||
version = "0.18.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-tungstenite",
|
||||
@ -2104,6 +2098,15 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_refineable"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dhat"
|
||||
version = "0.3.2"
|
||||
@ -3086,6 +3089,7 @@ dependencies = [
|
||||
"png",
|
||||
"postage",
|
||||
"rand 0.8.5",
|
||||
"refineable",
|
||||
"resvg",
|
||||
"schemars",
|
||||
"seahash",
|
||||
@ -3097,6 +3101,7 @@ dependencies = [
|
||||
"smol",
|
||||
"sqlez",
|
||||
"sum_tree",
|
||||
"taffy",
|
||||
"time 0.3.24",
|
||||
"tiny-skia",
|
||||
"usvg",
|
||||
@ -3109,11 +3114,18 @@ dependencies = [
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grid"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.20"
|
||||
@ -3903,17 +3915,6 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "lewton"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"ogg",
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.147"
|
||||
@ -4767,15 +4768,6 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ogg"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
@ -5126,6 +5118,33 @@ version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "playground"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_more",
|
||||
"gpui",
|
||||
"log",
|
||||
"parking_lot 0.11.2",
|
||||
"playground_macros",
|
||||
"refineable",
|
||||
"serde",
|
||||
"simplelog",
|
||||
"smallvec",
|
||||
"taffy",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "playground_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plist"
|
||||
version = "1.5.0"
|
||||
@ -5803,6 +5822,16 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "refineable"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"derive_refineable",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regalloc2"
|
||||
version = "0.2.3"
|
||||
@ -6021,11 +6050,8 @@ version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdf1d4dea18dff2e9eb6dca123724f8b60ef44ad74a9ad283cdfe025df7e73fa"
|
||||
dependencies = [
|
||||
"claxon",
|
||||
"cpal",
|
||||
"hound",
|
||||
"lewton",
|
||||
"symphonia",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6117,9 +6143,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "6.8.1"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661"
|
||||
checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
@ -6128,9 +6154,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "6.8.1"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac"
|
||||
checksum = "3c3d8c6fd84090ae348e63a84336b112b5c3918b3bf0493a581f7bd8ee623c29"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -6141,9 +6167,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "7.8.1"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74"
|
||||
checksum = "873feff8cb7bf86fdf0a71bb21c95159f4e4a37dd7a4bd1855a940909b583ada"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"sha2 0.10.7",
|
||||
@ -6971,6 +6997,15 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sluice"
|
||||
version = "0.5.5"
|
||||
@ -7341,56 +7376,6 @@ dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62e48dba70095f265fdb269b99619b95d04c89e619538138383e63310b14d941"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"symphonia-bundle-mp3",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-bundle-mp3"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f31d7fece546f1e6973011a9eceae948133bbd18fd3d52f6073b1e38ae6368a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
"symphonia-metadata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-core"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c73eb88fee79705268cc7b742c7bc93a7b76e092ab751d0833866970754142"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.4",
|
||||
"bitflags 1.3.2",
|
||||
"bytemuck",
|
||||
"lazy_static",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symphonia-metadata"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89c3e1937e31d0e068bbe829f66b2f2bfaa28d056365279e0ef897172c3320c0"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"symphonia-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@ -7460,6 +7445,17 @@ dependencies = [
|
||||
"winx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "taffy"
|
||||
version = "0.3.11"
|
||||
source = "git+https://github.com/DioxusLabs/taffy?rev=dab541d6104d58e2e10ce90c4a1dad0b703160cd#dab541d6104d58e2e10ce90c4a1dad0b703160cd"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.4",
|
||||
"grid",
|
||||
"num-traits",
|
||||
"slotmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take-until"
|
||||
version = "0.2.0"
|
||||
@ -9649,7 +9645,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.101.0"
|
||||
version = "0.102.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"ai",
|
||||
|
@ -18,6 +18,8 @@ members = [
|
||||
"crates/copilot",
|
||||
"crates/copilot_button",
|
||||
"crates/db",
|
||||
"crates/refineable",
|
||||
"crates/refineable/derive_refineable",
|
||||
"crates/diagnostics",
|
||||
"crates/drag_and_drop",
|
||||
"crates/editor",
|
||||
@ -29,6 +31,8 @@ members = [
|
||||
"crates/git",
|
||||
"crates/go_to_line",
|
||||
"crates/gpui",
|
||||
"crates/gpui/playground",
|
||||
"crates/gpui/playground_macros",
|
||||
"crates/gpui_macros",
|
||||
"crates/install_cli",
|
||||
"crates/journal",
|
||||
@ -93,8 +97,9 @@ ordered-float = { version = "2.1.1" }
|
||||
parking_lot = { version = "0.11.1" }
|
||||
postage = { version = "0.5", features = ["futures-traits"] }
|
||||
rand = { version = "0.8.5" }
|
||||
refineable = { path = "./crates/refineable" }
|
||||
regex = { version = "1.5" }
|
||||
rust-embed = { version = "6.3", features = ["include-exclude"] }
|
||||
rust-embed = { version = "8.0", features = ["include-exclude"] }
|
||||
schemars = { version = "0.8" }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
|
||||
|
@ -543,6 +543,8 @@
|
||||
"bindings": {
|
||||
"left": "project_panel::CollapseSelectedEntry",
|
||||
"right": "project_panel::ExpandSelectedEntry",
|
||||
"cmd-n": "project_panel::NewFile",
|
||||
"alt-cmd-n": "project_panel::NewDirectory",
|
||||
"cmd-x": "project_panel::Cut",
|
||||
"cmd-c": "project_panel::Copy",
|
||||
"cmd-v": "project_panel::Paste",
|
||||
|
@ -2,7 +2,6 @@
|
||||
{
|
||||
"bindings": {
|
||||
"cmd-shift-o": "projects::OpenRecent",
|
||||
"cmd-shift-b": "branches::OpenRecent",
|
||||
"cmd-alt-tab": "project_panel::ToggleFocus"
|
||||
}
|
||||
},
|
||||
@ -12,8 +11,9 @@
|
||||
"cmd-l": "go_to_line::Toggle",
|
||||
"ctrl-shift-d": "editor::DuplicateLine",
|
||||
"cmd-b": "editor::GoToDefinition",
|
||||
"alt-cmd-b": "editor::GoToDefinition",
|
||||
"cmd-j": "editor::ScrollCursorCenter",
|
||||
"cmd-enter": "editor::NewlineBelow",
|
||||
"cmd-alt-enter": "editor::NewLineAbove",
|
||||
"cmd-shift-l": "editor::SelectLine",
|
||||
"cmd-shift-t": "outline::Toggle",
|
||||
"alt-backspace": "editor::DeleteToPreviousWordStart",
|
||||
@ -51,14 +51,17 @@
|
||||
}
|
||||
],
|
||||
"ctrl-shift-left": "editor::SelectToPreviousSubwordStart",
|
||||
"ctrl-shift-right": "editor::SelectToNextSubwordEnd"
|
||||
"ctrl-shift-right": "editor::SelectToNextSubwordEnd",
|
||||
"ctrl-w": "editor::SelectNext",
|
||||
"ctrl-u": "editor::ConvertToUpperCase",
|
||||
"ctrl-shift-u": "editor::ConvertToLowerCase",
|
||||
"ctrl-alt-u": "editor::ConvertToUpperCamelCase",
|
||||
"ctrl-_": "editor::ConvertToSnakeCase"
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && mode == full",
|
||||
"bindings": {
|
||||
"cmd-alt-enter": "editor::NewlineAbove"
|
||||
}
|
||||
"bindings": {}
|
||||
},
|
||||
{
|
||||
"context": "BufferSearchBar",
|
||||
@ -85,5 +88,9 @@
|
||||
{
|
||||
"context": "ProjectPanel",
|
||||
"bindings": {}
|
||||
},
|
||||
{
|
||||
"context": "Dock",
|
||||
"bindings": {}
|
||||
}
|
||||
]
|
||||
|
@ -287,6 +287,12 @@
|
||||
"shift-o": "vim::InsertLineAbove",
|
||||
"~": "vim::ChangeCase",
|
||||
"p": "vim::Paste",
|
||||
"shift-p": [
|
||||
"vim::Paste",
|
||||
{
|
||||
"before": true
|
||||
}
|
||||
],
|
||||
"u": "editor::Undo",
|
||||
"ctrl-r": "editor::Redo",
|
||||
"/": "vim::Search",
|
||||
@ -375,7 +381,13 @@
|
||||
"d": "vim::VisualDelete",
|
||||
"x": "vim::VisualDelete",
|
||||
"y": "vim::VisualYank",
|
||||
"p": "vim::VisualPaste",
|
||||
"p": "vim::Paste",
|
||||
"shift-p": [
|
||||
"vim::Paste",
|
||||
{
|
||||
"preserveClipboard": true
|
||||
}
|
||||
],
|
||||
"s": "vim::Substitute",
|
||||
"c": "vim::Substitute",
|
||||
"~": "vim::ChangeCase",
|
||||
@ -421,7 +433,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "Editor && vim_mode == insert",
|
||||
"context": "Editor && vim_mode == insert && !menu",
|
||||
"bindings": {
|
||||
"escape": "vim::NormalBefore",
|
||||
"ctrl-c": "vim::NormalBefore",
|
||||
|
@ -138,7 +138,13 @@
|
||||
// Default width when the assistant is docked to the left or right.
|
||||
"default_width": 640,
|
||||
// Default height when the assistant is docked to the bottom.
|
||||
"default_height": 320
|
||||
"default_height": 320,
|
||||
// The default OpenAI model to use when starting new conversations. This
|
||||
// setting can take two values:
|
||||
//
|
||||
// 1. "gpt-3.5-turbo-0613""
|
||||
// 2. "gpt-4-0613""
|
||||
"default_open_ai_model": "gpt-4-0613"
|
||||
},
|
||||
// Whether the screen sharing icon is shown in the os status bar.
|
||||
"show_call_status_icon": true,
|
||||
|
@ -3,6 +3,7 @@ mod assistant_settings;
|
||||
|
||||
use anyhow::Result;
|
||||
pub use assistant::AssistantPanel;
|
||||
use assistant_settings::OpenAIModel;
|
||||
use chrono::{DateTime, Local};
|
||||
use collections::HashMap;
|
||||
use fs::Fs;
|
||||
@ -60,7 +61,7 @@ struct SavedConversation {
|
||||
messages: Vec<SavedMessage>,
|
||||
message_metadata: HashMap<MessageId, MessageMetadata>,
|
||||
summary: String,
|
||||
model: String,
|
||||
model: OpenAIModel,
|
||||
}
|
||||
|
||||
impl SavedConversation {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
assistant_settings::{AssistantDockPosition, AssistantSettings},
|
||||
assistant_settings::{AssistantDockPosition, AssistantSettings, OpenAIModel},
|
||||
MessageId, MessageMetadata, MessageStatus, OpenAIRequest, OpenAIResponseStreamEvent,
|
||||
RequestMessage, Role, SavedConversation, SavedConversationMetadata, SavedMessage,
|
||||
};
|
||||
@ -833,7 +833,7 @@ struct Conversation {
|
||||
pending_summary: Task<Option<()>>,
|
||||
completion_count: usize,
|
||||
pending_completions: Vec<PendingCompletion>,
|
||||
model: String,
|
||||
model: OpenAIModel,
|
||||
token_count: Option<usize>,
|
||||
max_token_count: usize,
|
||||
pending_token_count: Task<Option<()>>,
|
||||
@ -853,7 +853,6 @@ impl Conversation {
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
let model = "gpt-3.5-turbo-0613";
|
||||
let markdown = language_registry.language_for_name("Markdown");
|
||||
let buffer = cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(0, "", cx);
|
||||
@ -872,6 +871,9 @@ impl Conversation {
|
||||
buffer
|
||||
});
|
||||
|
||||
let settings = settings::get::<AssistantSettings>(cx);
|
||||
let model = settings.default_open_ai_model.clone();
|
||||
|
||||
let mut this = Self {
|
||||
message_anchors: Default::default(),
|
||||
messages_metadata: Default::default(),
|
||||
@ -881,9 +883,9 @@ impl Conversation {
|
||||
completion_count: Default::default(),
|
||||
pending_completions: Default::default(),
|
||||
token_count: None,
|
||||
max_token_count: tiktoken_rs::model::get_context_size(model),
|
||||
max_token_count: tiktoken_rs::model::get_context_size(&model.full_name()),
|
||||
pending_token_count: Task::ready(None),
|
||||
model: model.into(),
|
||||
model: model.clone(),
|
||||
_subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
|
||||
pending_save: Task::ready(Ok(())),
|
||||
path: None,
|
||||
@ -977,7 +979,7 @@ impl Conversation {
|
||||
completion_count: Default::default(),
|
||||
pending_completions: Default::default(),
|
||||
token_count: None,
|
||||
max_token_count: tiktoken_rs::model::get_context_size(&model),
|
||||
max_token_count: tiktoken_rs::model::get_context_size(&model.full_name()),
|
||||
pending_token_count: Task::ready(None),
|
||||
model,
|
||||
_subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
|
||||
@ -1031,13 +1033,16 @@ impl Conversation {
|
||||
cx.background().timer(Duration::from_millis(200)).await;
|
||||
let token_count = cx
|
||||
.background()
|
||||
.spawn(async move { tiktoken_rs::num_tokens_from_messages(&model, &messages) })
|
||||
.spawn(async move {
|
||||
tiktoken_rs::num_tokens_from_messages(&model.full_name(), &messages)
|
||||
})
|
||||
.await?;
|
||||
|
||||
this.upgrade(&cx)
|
||||
.ok_or_else(|| anyhow!("conversation was dropped"))?
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.max_token_count = tiktoken_rs::model::get_context_size(&this.model);
|
||||
this.max_token_count =
|
||||
tiktoken_rs::model::get_context_size(&this.model.full_name());
|
||||
this.token_count = Some(token_count);
|
||||
cx.notify()
|
||||
});
|
||||
@ -1051,7 +1056,7 @@ impl Conversation {
|
||||
Some(self.max_token_count as isize - self.token_count? as isize)
|
||||
}
|
||||
|
||||
fn set_model(&mut self, model: String, cx: &mut ModelContext<Self>) {
|
||||
fn set_model(&mut self, model: OpenAIModel, cx: &mut ModelContext<Self>) {
|
||||
self.model = model;
|
||||
self.count_remaining_tokens(cx);
|
||||
cx.notify();
|
||||
@ -1093,7 +1098,7 @@ impl Conversation {
|
||||
}
|
||||
} else {
|
||||
let request = OpenAIRequest {
|
||||
model: self.model.clone(),
|
||||
model: self.model.full_name().to_string(),
|
||||
messages: self
|
||||
.messages(cx)
|
||||
.filter(|message| matches!(message.status, MessageStatus::Done))
|
||||
@ -1419,7 +1424,7 @@ impl Conversation {
|
||||
.into(),
|
||||
}));
|
||||
let request = OpenAIRequest {
|
||||
model: self.model.clone(),
|
||||
model: self.model.full_name().to_string(),
|
||||
messages: messages.collect(),
|
||||
stream: true,
|
||||
};
|
||||
@ -2023,11 +2028,8 @@ impl ConversationEditor {
|
||||
|
||||
fn cycle_model(&mut self, cx: &mut ViewContext<Self>) {
|
||||
self.conversation.update(cx, |conversation, cx| {
|
||||
let new_model = match conversation.model.as_str() {
|
||||
"gpt-4-0613" => "gpt-3.5-turbo-0613",
|
||||
_ => "gpt-4-0613",
|
||||
};
|
||||
conversation.set_model(new_model.into(), cx);
|
||||
let new_model = conversation.model.cycle();
|
||||
conversation.set_model(new_model, cx);
|
||||
});
|
||||
}
|
||||
|
||||
@ -2049,7 +2051,8 @@ impl ConversationEditor {
|
||||
|
||||
MouseEventHandler::new::<Model, _>(0, cx, |state, cx| {
|
||||
let style = style.model.style_for(state);
|
||||
Label::new(self.conversation.read(cx).model.clone(), style.text.clone())
|
||||
let model_display_name = self.conversation.read(cx).model.short_name();
|
||||
Label::new(model_display_name, style.text.clone())
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
})
|
||||
@ -2238,6 +2241,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_inserting_and_removing_messages(cx: &mut AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
init(cx);
|
||||
let registry = Arc::new(LanguageRegistry::test());
|
||||
let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
|
||||
let buffer = conversation.read(cx).buffer.clone();
|
||||
@ -2364,6 +2369,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_message_splitting(cx: &mut AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
init(cx);
|
||||
let registry = Arc::new(LanguageRegistry::test());
|
||||
let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
|
||||
let buffer = conversation.read(cx).buffer.clone();
|
||||
@ -2458,6 +2465,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_messages_for_offsets(cx: &mut AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
init(cx);
|
||||
let registry = Arc::new(LanguageRegistry::test());
|
||||
let conversation = cx.add_model(|cx| Conversation::new(Default::default(), registry, cx));
|
||||
let buffer = conversation.read(cx).buffer.clone();
|
||||
@ -2538,6 +2547,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_serialization(cx: &mut AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
init(cx);
|
||||
let registry = Arc::new(LanguageRegistry::test());
|
||||
let conversation =
|
||||
cx.add_model(|cx| Conversation::new(Default::default(), registry.clone(), cx));
|
||||
|
@ -3,6 +3,37 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Setting;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||
pub enum OpenAIModel {
|
||||
#[serde(rename = "gpt-3.5-turbo-0613")]
|
||||
ThreePointFiveTurbo,
|
||||
#[serde(rename = "gpt-4-0613")]
|
||||
Four,
|
||||
}
|
||||
|
||||
impl OpenAIModel {
|
||||
pub fn full_name(&self) -> &'static str {
|
||||
match self {
|
||||
OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo-0613",
|
||||
OpenAIModel::Four => "gpt-4-0613",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn short_name(&self) -> &'static str {
|
||||
match self {
|
||||
OpenAIModel::ThreePointFiveTurbo => "gpt-3.5-turbo",
|
||||
OpenAIModel::Four => "gpt-4",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle(&self) -> Self {
|
||||
match self {
|
||||
OpenAIModel::ThreePointFiveTurbo => OpenAIModel::Four,
|
||||
OpenAIModel::Four => OpenAIModel::ThreePointFiveTurbo,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AssistantDockPosition {
|
||||
@ -17,6 +48,7 @@ pub struct AssistantSettings {
|
||||
pub dock: AssistantDockPosition,
|
||||
pub default_width: f32,
|
||||
pub default_height: f32,
|
||||
pub default_open_ai_model: OpenAIModel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
@ -25,6 +57,7 @@ pub struct AssistantSettingsContent {
|
||||
pub dock: Option<AssistantDockPosition>,
|
||||
pub default_width: Option<f32>,
|
||||
pub default_height: Option<f32>,
|
||||
pub default_open_ai_model: Option<OpenAIModel>,
|
||||
}
|
||||
|
||||
impl Setting for AssistantSettings {
|
||||
|
@ -13,7 +13,7 @@ gpui = { path = "../gpui" }
|
||||
collections = { path = "../collections" }
|
||||
util = { path = "../util" }
|
||||
|
||||
rodio = "0.17.1"
|
||||
rodio ={version = "0.17.1", default-features=false, features = ["wav"]}
|
||||
|
||||
log.workspace = true
|
||||
|
||||
|
@ -274,7 +274,7 @@ impl ActiveCall {
|
||||
.borrow_mut()
|
||||
.take()
|
||||
.ok_or_else(|| anyhow!("no incoming call"))?;
|
||||
Self::report_call_event_for_room("decline incoming", call.room_id, &self.client, cx);
|
||||
Self::report_call_event_for_room("decline incoming", call.room_id, None, &self.client, cx);
|
||||
self.client.send(proto::DeclineCall {
|
||||
room_id: call.room_id,
|
||||
})?;
|
||||
@ -406,19 +406,31 @@ impl ActiveCall {
|
||||
|
||||
fn report_call_event(&self, operation: &'static str, cx: &AppContext) {
|
||||
if let Some(room) = self.room() {
|
||||
Self::report_call_event_for_room(operation, room.read(cx).id(), &self.client, cx)
|
||||
let room = room.read(cx);
|
||||
Self::report_call_event_for_room(
|
||||
operation,
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&self.client,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_call_event_for_room(
|
||||
operation: &'static str,
|
||||
room_id: u64,
|
||||
channel_id: Option<u64>,
|
||||
client: &Arc<Client>,
|
||||
cx: &AppContext,
|
||||
) {
|
||||
let telemetry = client.telemetry();
|
||||
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
|
||||
let event = ClickhouseEvent::Call { operation, room_id };
|
||||
let event = ClickhouseEvent::Call {
|
||||
operation,
|
||||
room_id,
|
||||
channel_id,
|
||||
};
|
||||
telemetry.report_clickhouse_event(event, telemetry_settings);
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,7 @@ pub enum ClickhouseEvent {
|
||||
Call {
|
||||
operation: &'static str,
|
||||
room_id: u64,
|
||||
channel_id: Option<u64>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ authors = ["Nathan Sobo <nathan@zed.dev>"]
|
||||
default-run = "collab"
|
||||
edition = "2021"
|
||||
name = "collab"
|
||||
version = "0.17.0"
|
||||
version = "0.18.0"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
|
@ -1096,7 +1096,7 @@ impl CollabTitlebarItem {
|
||||
style
|
||||
}
|
||||
|
||||
fn render_face<V: View>(
|
||||
fn render_face<V: 'static>(
|
||||
avatar: Arc<ImageData>,
|
||||
avatar_style: AvatarStyle,
|
||||
background_color: Color,
|
||||
|
@ -49,6 +49,7 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
|
||||
ActiveCall::report_call_event_for_room(
|
||||
"disable screen share",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
cx,
|
||||
);
|
||||
@ -57,6 +58,7 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
|
||||
ActiveCall::report_call_event_for_room(
|
||||
"enable screen share",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
cx,
|
||||
);
|
||||
@ -73,11 +75,18 @@ pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
|
||||
let client = call.client();
|
||||
room.update(cx, |room, cx| {
|
||||
if room.is_muted(cx) {
|
||||
ActiveCall::report_call_event_for_room("enable microphone", room.id(), &client, cx);
|
||||
ActiveCall::report_call_event_for_room(
|
||||
"enable microphone",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
ActiveCall::report_call_event_for_room(
|
||||
"disable microphone",
|
||||
room.id(),
|
||||
room.channel_id(),
|
||||
&client,
|
||||
cx,
|
||||
);
|
||||
|
@ -2,14 +2,14 @@ use client::User;
|
||||
use gpui::{
|
||||
elements::*,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AnyElement, Element, View, ViewContext,
|
||||
AnyElement, Element, ViewContext,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
enum Dismiss {}
|
||||
enum Button {}
|
||||
|
||||
pub fn render_user_notification<F, V>(
|
||||
pub fn render_user_notification<F, V: 'static>(
|
||||
user: Arc<User>,
|
||||
title: &'static str,
|
||||
body: Option<&'static str>,
|
||||
@ -19,7 +19,6 @@ pub fn render_user_notification<F, V>(
|
||||
) -> AnyElement<V>
|
||||
where
|
||||
F: 'static + Fn(&mut V, &mut ViewContext<V>),
|
||||
V: View,
|
||||
{
|
||||
let theme = theme::current(cx).clone();
|
||||
let theme = &theme.contact_notification;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use gpui::{
|
||||
actions,
|
||||
color::Color,
|
||||
elements::{Component, Flex, ParentElement, SafeStylable},
|
||||
AppContext, Element, Entity, ModelHandle, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
||||
};
|
||||
@ -97,7 +96,7 @@ impl View for ComponentTest {
|
||||
}
|
||||
|
||||
impl Item for ComponentTest {
|
||||
fn tab_content<V: View>(
|
||||
fn tab_content<V: 'static>(
|
||||
&self,
|
||||
_: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
|
@ -538,7 +538,7 @@ impl ProjectDiagnosticsEditor {
|
||||
}
|
||||
|
||||
impl Item for ProjectDiagnosticsEditor {
|
||||
fn tab_content<T: View>(
|
||||
fn tab_content<T: 'static>(
|
||||
&self,
|
||||
_detail: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
@ -735,7 +735,7 @@ fn diagnostic_header_renderer(diagnostic: Diagnostic) -> RenderBlock {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn render_summary<T: View>(
|
||||
pub(crate) fn render_summary<T: 'static>(
|
||||
summary: &DiagnosticSummary,
|
||||
text_style: &TextStyle,
|
||||
theme: &theme::ProjectDiagnostics,
|
||||
|
@ -11,7 +11,7 @@ use gpui::{
|
||||
|
||||
const DEAD_ZONE: f32 = 4.;
|
||||
|
||||
enum State<V: View> {
|
||||
enum State<V> {
|
||||
Down {
|
||||
region_offset: Vector2F,
|
||||
region: RectF,
|
||||
@ -31,7 +31,7 @@ enum State<V: View> {
|
||||
Canceled,
|
||||
}
|
||||
|
||||
impl<V: View> Clone for State<V> {
|
||||
impl<V> Clone for State<V> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
&State::Down {
|
||||
@ -68,12 +68,12 @@ impl<V: View> Clone for State<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DragAndDrop<V: View> {
|
||||
pub struct DragAndDrop<V> {
|
||||
containers: HashSet<WeakViewHandle<V>>,
|
||||
currently_dragged: Option<State<V>>,
|
||||
}
|
||||
|
||||
impl<V: View> Default for DragAndDrop<V> {
|
||||
impl<V> Default for DragAndDrop<V> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
containers: Default::default(),
|
||||
@ -82,7 +82,7 @@ impl<V: View> Default for DragAndDrop<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> DragAndDrop<V> {
|
||||
impl<V: 'static> DragAndDrop<V> {
|
||||
pub fn register_container(&mut self, handle: WeakViewHandle<V>) {
|
||||
self.containers.insert(handle);
|
||||
}
|
||||
@ -291,7 +291,7 @@ impl<V: View> DragAndDrop<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Draggable<V: View> {
|
||||
pub trait Draggable<V> {
|
||||
fn as_draggable<D: View, P: Any>(
|
||||
self,
|
||||
payload: P,
|
||||
@ -301,7 +301,7 @@ pub trait Draggable<V: View> {
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl<V: View> Draggable<V> for MouseEventHandler<V> {
|
||||
impl<V: 'static> Draggable<V> for MouseEventHandler<V> {
|
||||
fn as_draggable<D: View, P: Any>(
|
||||
self,
|
||||
payload: P,
|
||||
|
@ -1736,6 +1736,31 @@ impl Editor {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn edit_with_block_indent<I, S, T>(
|
||||
&mut self,
|
||||
edits: I,
|
||||
original_indent_columns: Vec<u32>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) where
|
||||
I: IntoIterator<Item = (Range<S>, T)>,
|
||||
S: ToOffset,
|
||||
T: Into<Arc<str>>,
|
||||
{
|
||||
if self.read_only {
|
||||
return;
|
||||
}
|
||||
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
edits,
|
||||
Some(AutoindentMode::Block {
|
||||
original_indent_columns,
|
||||
}),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn select(&mut self, phase: SelectPhase, cx: &mut ViewContext<Self>) {
|
||||
self.hide_context_menu(cx);
|
||||
|
||||
@ -2667,7 +2692,6 @@ impl Editor {
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
|
||||
let offset = position.to_offset(buffer);
|
||||
let (word_range, kind) = buffer.surrounding_word(offset);
|
||||
@ -4742,6 +4766,7 @@ impl Editor {
|
||||
let mut clipboard_selections = Vec::with_capacity(selections.len());
|
||||
{
|
||||
let max_point = buffer.max_point();
|
||||
let mut is_first = true;
|
||||
for selection in &mut selections {
|
||||
let is_entire_line = selection.is_empty() || self.selections.line_mode;
|
||||
if is_entire_line {
|
||||
@ -4749,6 +4774,11 @@ impl Editor {
|
||||
selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
|
||||
selection.goal = SelectionGoal::None;
|
||||
}
|
||||
if is_first {
|
||||
is_first = false;
|
||||
} else {
|
||||
text += "\n";
|
||||
}
|
||||
let mut len = 0;
|
||||
for chunk in buffer.text_for_range(selection.start..selection.end) {
|
||||
text.push_str(chunk);
|
||||
@ -4779,6 +4809,7 @@ impl Editor {
|
||||
let mut clipboard_selections = Vec::with_capacity(selections.len());
|
||||
{
|
||||
let max_point = buffer.max_point();
|
||||
let mut is_first = true;
|
||||
for selection in selections.iter() {
|
||||
let mut start = selection.start;
|
||||
let mut end = selection.end;
|
||||
@ -4787,6 +4818,11 @@ impl Editor {
|
||||
start = Point::new(start.row, 0);
|
||||
end = cmp::min(max_point, Point::new(end.row + 1, 0));
|
||||
}
|
||||
if is_first {
|
||||
is_first = false;
|
||||
} else {
|
||||
text += "\n";
|
||||
}
|
||||
let mut len = 0;
|
||||
for chunk in buffer.text_for_range(start..end) {
|
||||
text.push_str(chunk);
|
||||
@ -4806,7 +4842,7 @@ impl Editor {
|
||||
pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
|
||||
self.transact(cx, |this, cx| {
|
||||
if let Some(item) = cx.read_from_clipboard() {
|
||||
let mut clipboard_text = Cow::Borrowed(item.text());
|
||||
let clipboard_text = Cow::Borrowed(item.text());
|
||||
if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
|
||||
let old_selections = this.selections.all::<usize>(cx);
|
||||
let all_selections_were_entire_line =
|
||||
@ -4814,18 +4850,7 @@ impl Editor {
|
||||
let first_selection_indent_column =
|
||||
clipboard_selections.first().map(|s| s.first_line_indent);
|
||||
if clipboard_selections.len() != old_selections.len() {
|
||||
let mut newline_separated_text = String::new();
|
||||
let mut clipboard_selections = clipboard_selections.drain(..).peekable();
|
||||
let mut ix = 0;
|
||||
while let Some(clipboard_selection) = clipboard_selections.next() {
|
||||
newline_separated_text
|
||||
.push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
|
||||
ix += clipboard_selection.len;
|
||||
if clipboard_selections.peek().is_some() {
|
||||
newline_separated_text.push('\n');
|
||||
}
|
||||
}
|
||||
clipboard_text = Cow::Owned(newline_separated_text);
|
||||
clipboard_selections.drain(..);
|
||||
}
|
||||
|
||||
this.buffer.update(cx, |buffer, cx| {
|
||||
@ -4841,8 +4866,9 @@ impl Editor {
|
||||
if let Some(clipboard_selection) = clipboard_selections.get(ix) {
|
||||
let end_offset = start_offset + clipboard_selection.len;
|
||||
to_insert = &clipboard_text[start_offset..end_offset];
|
||||
dbg!(start_offset, end_offset, &clipboard_text, &to_insert);
|
||||
entire_line = clipboard_selection.is_entire_line;
|
||||
start_offset = end_offset;
|
||||
start_offset = end_offset + 1;
|
||||
original_indent_column =
|
||||
Some(clipboard_selection.first_line_indent);
|
||||
} else {
|
||||
@ -8537,6 +8563,7 @@ fn build_style(
|
||||
font_size,
|
||||
font_properties,
|
||||
underline: Default::default(),
|
||||
soft_wrap: false,
|
||||
},
|
||||
placeholder_text: None,
|
||||
line_height_scalar,
|
||||
|
@ -605,7 +605,7 @@ impl EditorElement {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut LayoutState,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
cx: &mut PaintContext<Editor>,
|
||||
) {
|
||||
let line_height = layout.position_map.line_height;
|
||||
|
||||
@ -760,7 +760,7 @@ impl EditorElement {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut LayoutState,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
cx: &mut PaintContext<Editor>,
|
||||
) {
|
||||
let style = &self.style;
|
||||
let local_replica_id = editor.replica_id(cx);
|
||||
@ -1337,7 +1337,7 @@ impl EditorElement {
|
||||
visible_bounds: RectF,
|
||||
layout: &mut LayoutState,
|
||||
editor: &mut Editor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
cx: &mut PaintContext<Editor>,
|
||||
) {
|
||||
let scroll_position = layout.position_map.snapshot.scroll_position();
|
||||
let scroll_left = scroll_position.x() * layout.position_map.em_width;
|
||||
|
@ -561,7 +561,7 @@ impl Item for Editor {
|
||||
}
|
||||
}
|
||||
|
||||
fn tab_content<T: View>(
|
||||
fn tab_content<T: 'static>(
|
||||
&self,
|
||||
detail: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
@ -1028,7 +1028,7 @@ impl SearchableItem for Editor {
|
||||
if let Some((_, _, excerpt_buffer)) = buffer.as_singleton() {
|
||||
ranges.extend(
|
||||
query
|
||||
.search(excerpt_buffer.as_rope())
|
||||
.search(excerpt_buffer, None)
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|range| {
|
||||
@ -1038,17 +1038,22 @@ impl SearchableItem for Editor {
|
||||
} else {
|
||||
for excerpt in buffer.excerpt_boundaries_in_range(0..buffer.len()) {
|
||||
let excerpt_range = excerpt.range.context.to_offset(&excerpt.buffer);
|
||||
let rope = excerpt.buffer.as_rope().slice(excerpt_range.clone());
|
||||
ranges.extend(query.search(&rope).await.into_iter().map(|range| {
|
||||
let start = excerpt
|
||||
.buffer
|
||||
.anchor_after(excerpt_range.start + range.start);
|
||||
let end = excerpt
|
||||
.buffer
|
||||
.anchor_before(excerpt_range.start + range.end);
|
||||
buffer.anchor_in_excerpt(excerpt.id.clone(), start)
|
||||
..buffer.anchor_in_excerpt(excerpt.id.clone(), end)
|
||||
}));
|
||||
ranges.extend(
|
||||
query
|
||||
.search(&excerpt.buffer, Some(excerpt_range.clone()))
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|range| {
|
||||
let start = excerpt
|
||||
.buffer
|
||||
.anchor_after(excerpt_range.start + range.start);
|
||||
let end = excerpt
|
||||
.buffer
|
||||
.anchor_before(excerpt_range.start + range.end);
|
||||
buffer.anchor_in_excerpt(excerpt.id.clone(), start)
|
||||
..buffer.anchor_in_excerpt(excerpt.id.clone(), end)
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
ranges
|
||||
|
@ -176,14 +176,21 @@ pub fn line_end(
|
||||
}
|
||||
|
||||
pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
|
||||
let raw_point = point.to_point(map);
|
||||
let language = map.buffer_snapshot.language_at(raw_point);
|
||||
|
||||
find_preceding_boundary(map, point, |left, right| {
|
||||
(char_kind(left) != char_kind(right) && !right.is_whitespace()) || left == '\n'
|
||||
(char_kind(language, left) != char_kind(language, right) && !right.is_whitespace())
|
||||
|| left == '\n'
|
||||
})
|
||||
}
|
||||
|
||||
pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
|
||||
let raw_point = point.to_point(map);
|
||||
let language = map.buffer_snapshot.language_at(raw_point);
|
||||
find_preceding_boundary(map, point, |left, right| {
|
||||
let is_word_start = char_kind(left) != char_kind(right) && !right.is_whitespace();
|
||||
let is_word_start =
|
||||
char_kind(language, left) != char_kind(language, right) && !right.is_whitespace();
|
||||
let is_subword_start =
|
||||
left == '_' && right != '_' || left.is_lowercase() && right.is_uppercase();
|
||||
is_word_start || is_subword_start || left == '\n'
|
||||
@ -191,14 +198,20 @@ pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> Dis
|
||||
}
|
||||
|
||||
pub fn next_word_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
|
||||
let raw_point = point.to_point(map);
|
||||
let language = map.buffer_snapshot.language_at(raw_point);
|
||||
find_boundary(map, point, |left, right| {
|
||||
(char_kind(left) != char_kind(right) && !left.is_whitespace()) || right == '\n'
|
||||
(char_kind(language, left) != char_kind(language, right) && !left.is_whitespace())
|
||||
|| right == '\n'
|
||||
})
|
||||
}
|
||||
|
||||
pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
|
||||
let raw_point = point.to_point(map);
|
||||
let language = map.buffer_snapshot.language_at(raw_point);
|
||||
find_boundary(map, point, |left, right| {
|
||||
let is_word_end = (char_kind(left) != char_kind(right)) && !left.is_whitespace();
|
||||
let is_word_end =
|
||||
(char_kind(language, left) != char_kind(language, right)) && !left.is_whitespace();
|
||||
let is_subword_end =
|
||||
left != '_' && right == '_' || left.is_lowercase() && right.is_uppercase();
|
||||
is_word_end || is_subword_end || right == '\n'
|
||||
@ -385,10 +398,15 @@ pub fn find_boundary_in_line(
|
||||
}
|
||||
|
||||
pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
|
||||
let raw_point = point.to_point(map);
|
||||
let language = map.buffer_snapshot.language_at(raw_point);
|
||||
let ix = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left);
|
||||
let text = &map.buffer_snapshot;
|
||||
let next_char_kind = text.chars_at(ix).next().map(char_kind);
|
||||
let prev_char_kind = text.reversed_chars_at(ix).next().map(char_kind);
|
||||
let next_char_kind = text.chars_at(ix).next().map(|c| char_kind(language, c));
|
||||
let prev_char_kind = text
|
||||
.reversed_chars_at(ix)
|
||||
.next()
|
||||
.map(|c| char_kind(language, c));
|
||||
prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
|
||||
}
|
||||
|
||||
|
@ -1346,10 +1346,7 @@ impl MultiBuffer {
|
||||
.map(|state| state.buffer.clone())
|
||||
}
|
||||
|
||||
pub fn is_completion_trigger<T>(&self, position: T, text: &str, cx: &AppContext) -> bool
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
pub fn is_completion_trigger(&self, position: Anchor, text: &str, cx: &AppContext) -> bool {
|
||||
let mut chars = text.chars();
|
||||
let char = if let Some(char) = chars.next() {
|
||||
char
|
||||
@ -1360,7 +1357,9 @@ impl MultiBuffer {
|
||||
return false;
|
||||
}
|
||||
|
||||
if char.is_alphanumeric() || char == '_' {
|
||||
let language = self.language_at(position.clone(), cx);
|
||||
|
||||
if char_kind(language.as_ref(), char) == CharKind::Word {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1865,13 +1864,16 @@ impl MultiBufferSnapshot {
|
||||
let mut end = start;
|
||||
let mut next_chars = self.chars_at(start).peekable();
|
||||
let mut prev_chars = self.reversed_chars_at(start).peekable();
|
||||
|
||||
let language = self.language_at(start);
|
||||
let kind = |c| char_kind(language, c);
|
||||
let word_kind = cmp::max(
|
||||
prev_chars.peek().copied().map(char_kind),
|
||||
next_chars.peek().copied().map(char_kind),
|
||||
prev_chars.peek().copied().map(kind),
|
||||
next_chars.peek().copied().map(kind),
|
||||
);
|
||||
|
||||
for ch in prev_chars {
|
||||
if Some(char_kind(ch)) == word_kind && ch != '\n' {
|
||||
if Some(kind(ch)) == word_kind && ch != '\n' {
|
||||
start -= ch.len_utf8();
|
||||
} else {
|
||||
break;
|
||||
@ -1879,7 +1881,7 @@ impl MultiBufferSnapshot {
|
||||
}
|
||||
|
||||
for ch in next_chars {
|
||||
if Some(char_kind(ch)) == word_kind && ch != '\n' {
|
||||
if Some(kind(ch)) == word_kind && ch != '\n' {
|
||||
end += ch.len_utf8();
|
||||
} else {
|
||||
break;
|
||||
|
@ -6,6 +6,7 @@ use std::{
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use collections::HashSet;
|
||||
use futures::Future;
|
||||
use gpui::{json, ViewContext, ViewHandle};
|
||||
use indoc::indoc;
|
||||
@ -154,10 +155,23 @@ impl<'a> EditorLspTestContext<'a> {
|
||||
capabilities: lsp::ServerCapabilities,
|
||||
cx: &'a mut gpui::TestAppContext,
|
||||
) -> EditorLspTestContext<'a> {
|
||||
let mut word_characters: HashSet<char> = Default::default();
|
||||
word_characters.insert('$');
|
||||
word_characters.insert('#');
|
||||
let language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Typescript".into(),
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
brackets: language::BracketPairConfig {
|
||||
pairs: vec![language::BracketPair {
|
||||
start: "{".to_string(),
|
||||
end: "}".to_string(),
|
||||
close: true,
|
||||
newline: true,
|
||||
}],
|
||||
disabled_scopes_by_bracket_ix: Default::default(),
|
||||
},
|
||||
word_characters,
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_typescript::language_typescript()),
|
||||
@ -169,6 +183,23 @@ impl<'a> EditorLspTestContext<'a> {
|
||||
("{" @open "}" @close)
|
||||
("<" @open ">" @close)
|
||||
("\"" @open "\"" @close)"#})),
|
||||
indents: Some(Cow::from(indoc! {r#"
|
||||
[
|
||||
(call_expression)
|
||||
(assignment_expression)
|
||||
(member_expression)
|
||||
(lexical_declaration)
|
||||
(variable_declaration)
|
||||
(assignment_expression)
|
||||
(if_statement)
|
||||
(for_statement)
|
||||
] @indent
|
||||
|
||||
(_ "[" "]" @end) @indent
|
||||
(_ "<" ">" @end) @indent
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
||||
"#})),
|
||||
..Default::default()
|
||||
})
|
||||
.expect("Could not parse queries");
|
||||
|
@ -268,7 +268,7 @@ impl Item for FeedbackEditor {
|
||||
Some("Send Feedback".into())
|
||||
}
|
||||
|
||||
fn tab_content<T: View>(
|
||||
fn tab_content<T: 'static>(
|
||||
&self,
|
||||
_: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
|
@ -39,6 +39,7 @@ pathfinder_color = "0.5"
|
||||
pathfinder_geometry = "0.5"
|
||||
postage.workspace = true
|
||||
rand.workspace = true
|
||||
refineable.workspace = true
|
||||
resvg = "0.14"
|
||||
schemars = "0.8"
|
||||
seahash = "4.1"
|
||||
@ -47,6 +48,7 @@ serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
|
||||
time.workspace = true
|
||||
tiny-skia = "0.5"
|
||||
usvg = { version = "0.14", features = [] }
|
||||
|
@ -58,6 +58,7 @@ impl gpui::View for TextView {
|
||||
font_family_id: family,
|
||||
underline: Default::default(),
|
||||
font_properties: Default::default(),
|
||||
soft_wrap: false,
|
||||
},
|
||||
)
|
||||
.with_highlights(vec![(17..26, underline), (34..40, underline)])
|
||||
|
2919
crates/gpui/playground/Cargo.lock
generated
Normal file
2919
crates/gpui/playground/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
crates/gpui/playground/Cargo.toml
Normal file
26
crates/gpui/playground/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "playground"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "playground"
|
||||
path = "src/playground.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
derive_more.workspace = true
|
||||
gpui = { path = ".." }
|
||||
log.workspace = true
|
||||
playground_macros = { path = "../playground_macros" }
|
||||
parking_lot.workspace = true
|
||||
refineable.workspace = true
|
||||
serde.workspace = true
|
||||
simplelog = "0.9"
|
||||
smallvec.workspace = true
|
||||
taffy = { git = "https://github.com/DioxusLabs/taffy", rev = "dab541d6104d58e2e10ce90c4a1dad0b703160cd", features = ["flexbox"] }
|
||||
util = { path = "../../util" }
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { path = "..", features = ["test-support"] }
|
72
crates/gpui/playground/docs/thoughts.md
Normal file
72
crates/gpui/playground/docs/thoughts.md
Normal file
@ -0,0 +1,72 @@
|
||||
Much of element styling is now handled by an external engine.
|
||||
|
||||
|
||||
How do I make an element hover.
|
||||
|
||||
There's a hover style.
|
||||
|
||||
Hoverable needs to wrap another element. That element can be styled.
|
||||
|
||||
```rs
|
||||
struct Hoverable<E: Element> {
|
||||
|
||||
}
|
||||
|
||||
impl<V> Element<V> for Hoverable {
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
```rs
|
||||
#[derive(Styled, Interactive)]
|
||||
pub struct Div {
|
||||
declared_style: StyleRefinement,
|
||||
interactions: Interactions
|
||||
}
|
||||
|
||||
pub trait Styled {
|
||||
fn declared_style(&mut self) -> &mut StyleRefinement;
|
||||
fn compute_style(&mut self) -> Style {
|
||||
Style::default().refine(self.declared_style())
|
||||
}
|
||||
|
||||
// All the tailwind classes, modifying self.declared_style()
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn paint_background<V>(layout: Layout, cx: &mut PaintContext<V>);
|
||||
pub fn paint_foreground<V>(layout: Layout, cx: &mut PaintContext<V>);
|
||||
}
|
||||
|
||||
pub trait Interactive<V> {
|
||||
fn interactions(&mut self) -> &mut Interactions<V>;
|
||||
|
||||
fn on_click(self, )
|
||||
}
|
||||
|
||||
struct Interactions<V> {
|
||||
click: SmallVec<[<Rc<dyn Fn(&mut V, &dyn Any, )>; 1]>,
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
```rs
|
||||
|
||||
|
||||
trait Stylable {
|
||||
type Style;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
78
crates/gpui/playground/src/adapter.rs
Normal file
78
crates/gpui/playground/src/adapter.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use crate::{layout_context::LayoutContext, paint_context::PaintContext};
|
||||
use gpui::{geometry::rect::RectF, LayoutEngine, LayoutId};
|
||||
use util::ResultExt;
|
||||
|
||||
/// Makes a new, playground-style element into a legacy element.
|
||||
pub struct AdapterElement<V>(pub(crate) crate::element::AnyElement<V>);
|
||||
|
||||
impl<V: 'static> gpui::Element<V> for AdapterElement<V> {
|
||||
type LayoutState = Option<(LayoutEngine, LayoutId)>;
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: gpui::SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut gpui::LayoutContext<V>,
|
||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||
cx.push_layout_engine(LayoutEngine::new());
|
||||
|
||||
let size = constraint.max;
|
||||
let mut cx = LayoutContext::new(cx);
|
||||
let layout_id = self.0.layout(view, &mut cx).log_err();
|
||||
if let Some(layout_id) = layout_id {
|
||||
cx.layout_engine()
|
||||
.unwrap()
|
||||
.compute_layout(layout_id, constraint.max)
|
||||
.log_err();
|
||||
}
|
||||
|
||||
let layout_engine = cx.pop_layout_engine();
|
||||
debug_assert!(layout_engine.is_some(),
|
||||
"unexpected layout stack state. is there an unmatched pop_layout_engine in the called code?"
|
||||
);
|
||||
|
||||
(constraint.max, layout_engine.zip(layout_id))
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut gpui::SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
layout_data: &mut Option<(LayoutEngine, LayoutId)>,
|
||||
view: &mut V,
|
||||
legacy_cx: &mut gpui::PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let (layout_engine, layout_id) = layout_data.take().unwrap();
|
||||
legacy_cx.push_layout_engine(layout_engine);
|
||||
let mut cx = PaintContext::new(legacy_cx, scene);
|
||||
self.0.paint(view, &mut cx);
|
||||
*layout_data = legacy_cx.pop_layout_engine().zip(Some(layout_id));
|
||||
debug_assert!(layout_data.is_some());
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
range_utf16: std::ops::Range<usize>,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
layout: &Self::LayoutState,
|
||||
paint: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &gpui::ViewContext<V>,
|
||||
) -> Option<RectF> {
|
||||
todo!("implement before merging to main")
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
bounds: RectF,
|
||||
layout: &Self::LayoutState,
|
||||
paint: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &gpui::ViewContext<V>,
|
||||
) -> gpui::serde_json::Value {
|
||||
todo!("implement before merging to main")
|
||||
}
|
||||
}
|
276
crates/gpui/playground/src/color.rs
Normal file
276
crates/gpui/playground/src/color.rs
Normal file
@ -0,0 +1,276 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::{num::ParseIntError, ops::Range};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub fn rgb<C: From<Rgba>>(hex: u32) -> C {
|
||||
let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
|
||||
let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
|
||||
let b = (hex & 0xFF) as f32 / 255.0;
|
||||
Rgba { r, g, b, a: 1.0 }.into()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct Rgba {
|
||||
pub r: f32,
|
||||
pub g: f32,
|
||||
pub b: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
pub trait Lerp {
|
||||
fn lerp(&self, level: f32) -> Hsla;
|
||||
}
|
||||
|
||||
impl Lerp for Range<Hsla> {
|
||||
fn lerp(&self, level: f32) -> Hsla {
|
||||
let level = level.clamp(0., 1.);
|
||||
Hsla {
|
||||
h: self.start.h + (level * (self.end.h - self.start.h)),
|
||||
s: self.start.s + (level * (self.end.s - self.start.s)),
|
||||
l: self.start.l + (level * (self.end.l - self.start.l)),
|
||||
a: self.start.a + (level * (self.end.a - self.start.a)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gpui::color::Color> for Rgba {
|
||||
fn from(value: gpui::color::Color) -> Self {
|
||||
Self {
|
||||
r: value.0.r as f32 / 255.0,
|
||||
g: value.0.g as f32 / 255.0,
|
||||
b: value.0.b as f32 / 255.0,
|
||||
a: value.0.a as f32 / 255.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hsla> for Rgba {
|
||||
fn from(color: Hsla) -> Self {
|
||||
let h = color.h;
|
||||
let s = color.s;
|
||||
let l = color.l;
|
||||
|
||||
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
|
||||
let x = c * (1.0 - ((h * 6.0) % 2.0 - 1.0).abs());
|
||||
let m = l - c / 2.0;
|
||||
let cm = c + m;
|
||||
let xm = x + m;
|
||||
|
||||
let (r, g, b) = match (h * 6.0).floor() as i32 {
|
||||
0 | 6 => (cm, xm, m),
|
||||
1 => (xm, cm, m),
|
||||
2 => (m, cm, xm),
|
||||
3 => (m, xm, cm),
|
||||
4 => (xm, m, cm),
|
||||
_ => (cm, m, xm),
|
||||
};
|
||||
|
||||
Rgba {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
a: color.a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&'_ str> for Rgba {
|
||||
type Error = ParseIntError;
|
||||
|
||||
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
|
||||
let r = u8::from_str_radix(&value[1..3], 16)? as f32 / 255.0;
|
||||
let g = u8::from_str_radix(&value[3..5], 16)? as f32 / 255.0;
|
||||
let b = u8::from_str_radix(&value[5..7], 16)? as f32 / 255.0;
|
||||
let a = if value.len() > 7 {
|
||||
u8::from_str_radix(&value[7..9], 16)? as f32 / 255.0
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
Ok(Rgba { r, g, b, a })
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<gpui::color::Color> for Rgba {
|
||||
fn into(self) -> gpui::color::Color {
|
||||
gpui::color::rgba(self.r, self.g, self.b, self.a)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Hsla {
|
||||
pub h: f32,
|
||||
pub s: f32,
|
||||
pub l: f32,
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
|
||||
Hsla {
|
||||
h: h.clamp(0., 1.),
|
||||
s: s.clamp(0., 1.),
|
||||
l: l.clamp(0., 1.),
|
||||
a: a.clamp(0., 1.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn black() -> Hsla {
|
||||
Hsla {
|
||||
h: 0.,
|
||||
s: 0.,
|
||||
l: 0.,
|
||||
a: 1.,
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rgba> for Hsla {
|
||||
fn from(color: Rgba) -> Self {
|
||||
let r = color.r;
|
||||
let g = color.g;
|
||||
let b = color.b;
|
||||
|
||||
let max = r.max(g.max(b));
|
||||
let min = r.min(g.min(b));
|
||||
let delta = max - min;
|
||||
|
||||
let l = (max + min) / 2.0;
|
||||
let s = if l == 0.0 || l == 1.0 {
|
||||
0.0
|
||||
} else if l < 0.5 {
|
||||
delta / (2.0 * l)
|
||||
} else {
|
||||
delta / (2.0 - 2.0 * l)
|
||||
};
|
||||
|
||||
let h = if delta == 0.0 {
|
||||
0.0
|
||||
} else if max == r {
|
||||
((g - b) / delta).rem_euclid(6.0) / 6.0
|
||||
} else if max == g {
|
||||
((b - r) / delta + 2.0) / 6.0
|
||||
} else {
|
||||
((r - g) / delta + 4.0) / 6.0
|
||||
};
|
||||
|
||||
Hsla {
|
||||
h,
|
||||
s,
|
||||
l,
|
||||
a: color.a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hsla {
|
||||
/// Scales the saturation and lightness by the given values, clamping at 1.0.
|
||||
pub fn scale_sl(mut self, s: f32, l: f32) -> Self {
|
||||
self.s = (self.s * s).clamp(0., 1.);
|
||||
self.l = (self.l * l).clamp(0., 1.);
|
||||
self
|
||||
}
|
||||
|
||||
/// Increases the saturation of the color by a certain amount, with a max
|
||||
/// value of 1.0.
|
||||
pub fn saturate(mut self, amount: f32) -> Self {
|
||||
self.s += amount;
|
||||
self.s = self.s.clamp(0.0, 1.0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Decreases the saturation of the color by a certain amount, with a min
|
||||
/// value of 0.0.
|
||||
pub fn desaturate(mut self, amount: f32) -> Self {
|
||||
self.s -= amount;
|
||||
self.s = self.s.max(0.0);
|
||||
if self.s < 0.0 {
|
||||
self.s = 0.0;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Brightens the color by increasing the lightness by a certain amount,
|
||||
/// with a max value of 1.0.
|
||||
pub fn brighten(mut self, amount: f32) -> Self {
|
||||
self.l += amount;
|
||||
self.l = self.l.clamp(0.0, 1.0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Darkens the color by decreasing the lightness by a certain amount,
|
||||
/// with a max value of 0.0.
|
||||
pub fn darken(mut self, amount: f32) -> Self {
|
||||
self.l -= amount;
|
||||
self.l = self.l.clamp(0.0, 1.0);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<gpui::color::Color> for Hsla {
|
||||
fn from(value: gpui::color::Color) -> Self {
|
||||
Rgba::from(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<gpui::color::Color> for Hsla {
|
||||
fn into(self) -> gpui::color::Color {
|
||||
Rgba::from(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ColorScale {
|
||||
colors: SmallVec<[Hsla; 2]>,
|
||||
positions: SmallVec<[f32; 2]>,
|
||||
}
|
||||
|
||||
pub fn scale<I, C>(colors: I) -> ColorScale
|
||||
where
|
||||
I: IntoIterator<Item = C>,
|
||||
C: Into<Hsla>,
|
||||
{
|
||||
let mut scale = ColorScale {
|
||||
colors: colors.into_iter().map(Into::into).collect(),
|
||||
positions: SmallVec::new(),
|
||||
};
|
||||
let num_colors: f32 = scale.colors.len() as f32 - 1.0;
|
||||
scale.positions = (0..scale.colors.len())
|
||||
.map(|i| i as f32 / num_colors)
|
||||
.collect();
|
||||
scale
|
||||
}
|
||||
|
||||
impl ColorScale {
|
||||
fn at(&self, t: f32) -> Hsla {
|
||||
// Ensure that the input is within [0.0, 1.0]
|
||||
debug_assert!(
|
||||
0.0 <= t && t <= 1.0,
|
||||
"t value {} is out of range. Expected value in range 0.0 to 1.0",
|
||||
t
|
||||
);
|
||||
|
||||
let position = match self
|
||||
.positions
|
||||
.binary_search_by(|a| a.partial_cmp(&t).unwrap())
|
||||
{
|
||||
Ok(index) | Err(index) => index,
|
||||
};
|
||||
let lower_bound = position.saturating_sub(1);
|
||||
let upper_bound = position.min(self.colors.len() - 1);
|
||||
let lower_color = &self.colors[lower_bound];
|
||||
let upper_color = &self.colors[upper_bound];
|
||||
|
||||
match upper_bound.checked_sub(lower_bound) {
|
||||
Some(0) | None => *lower_color,
|
||||
Some(_) => {
|
||||
let interval_t = (t - self.positions[lower_bound])
|
||||
/ (self.positions[upper_bound] - self.positions[lower_bound]);
|
||||
let h = lower_color.h + interval_t * (upper_color.h - lower_color.h);
|
||||
let s = lower_color.s + interval_t * (upper_color.s - lower_color.s);
|
||||
let l = lower_color.l + interval_t * (upper_color.l - lower_color.l);
|
||||
let a = lower_color.a + interval_t * (upper_color.a - lower_color.a);
|
||||
Hsla { h, s, l, a }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
crates/gpui/playground/src/components.rs
Normal file
100
crates/gpui/playground/src/components.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use crate::{
|
||||
div::div,
|
||||
element::{Element, ParentElement},
|
||||
style::StyleHelpers,
|
||||
text::ArcCow,
|
||||
themes::rose_pine,
|
||||
};
|
||||
use gpui::ViewContext;
|
||||
use playground_macros::Element;
|
||||
use std::{marker::PhantomData, rc::Rc};
|
||||
|
||||
struct ButtonHandlers<V, D> {
|
||||
click: Option<Rc<dyn Fn(&mut V, &D, &mut ViewContext<V>)>>,
|
||||
}
|
||||
|
||||
impl<V, D> Default for ButtonHandlers<V, D> {
|
||||
fn default() -> Self {
|
||||
Self { click: None }
|
||||
}
|
||||
}
|
||||
|
||||
use crate as playground;
|
||||
#[derive(Element)]
|
||||
pub struct Button<V: 'static, D: 'static> {
|
||||
handlers: ButtonHandlers<V, D>,
|
||||
label: Option<ArcCow<'static, str>>,
|
||||
icon: Option<ArcCow<'static, str>>,
|
||||
data: Rc<D>,
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
// Impl block for buttons without data.
|
||||
// See below for an impl block for any button.
|
||||
impl<V: 'static> Button<V, ()> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
handlers: ButtonHandlers::default(),
|
||||
label: None,
|
||||
icon: None,
|
||||
data: Rc::new(()),
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn data<D: 'static>(self, data: D) -> Button<V, D> {
|
||||
Button {
|
||||
handlers: ButtonHandlers::default(),
|
||||
label: self.label,
|
||||
icon: self.icon,
|
||||
data: Rc::new(data),
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Impl block for *any* button.
|
||||
impl<V: 'static, D: 'static> Button<V, D> {
|
||||
pub fn label(mut self, label: impl Into<ArcCow<'static, str>>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon(mut self, icon: impl Into<ArcCow<'static, str>>) -> Self {
|
||||
self.icon = Some(icon.into());
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn click(self, handler: impl Fn(&mut V, &D, &mut ViewContext<V>) + 'static) -> Self {
|
||||
// let data = self.data.clone();
|
||||
// Self::click(self, MouseButton::Left, move |view, _, cx| {
|
||||
// handler(view, data.as_ref(), cx);
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn button<V>() -> Button<V, ()> {
|
||||
Button::new()
|
||||
}
|
||||
|
||||
impl<V: 'static, D: 'static> Button<V, D> {
|
||||
fn render(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
|
||||
// TODO: Drive theme from the context
|
||||
let button = div()
|
||||
.fill(rose_pine::dawn().error(0.5))
|
||||
.h_4()
|
||||
.children(self.label.clone());
|
||||
|
||||
button
|
||||
|
||||
// TODO: Event handling
|
||||
// if let Some(handler) = self.handlers.click.clone() {
|
||||
// let data = self.data.clone();
|
||||
// // button.mouse_down(MouseButton::Left, move |view, event, cx| {
|
||||
// // handler(view, data.as_ref(), cx)
|
||||
// // })
|
||||
// } else {
|
||||
// button
|
||||
// }
|
||||
}
|
||||
}
|
108
crates/gpui/playground/src/div.rs
Normal file
108
crates/gpui/playground/src/div.rs
Normal file
@ -0,0 +1,108 @@
|
||||
use crate::{
|
||||
element::{AnyElement, Element, Layout, ParentElement},
|
||||
interactive::{InteractionHandlers, Interactive},
|
||||
layout_context::LayoutContext,
|
||||
paint_context::PaintContext,
|
||||
style::{Style, StyleHelpers, StyleRefinement, Styleable},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gpui::LayoutId;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub struct Div<V: 'static> {
|
||||
style: StyleRefinement,
|
||||
handlers: InteractionHandlers<V>,
|
||||
children: SmallVec<[AnyElement<V>; 2]>,
|
||||
}
|
||||
|
||||
pub fn div<V>() -> Div<V> {
|
||||
Div {
|
||||
style: Default::default(),
|
||||
handlers: Default::default(),
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for Div<V> {
|
||||
type Layout = ();
|
||||
|
||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, ()>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let children = self
|
||||
.children
|
||||
.iter_mut()
|
||||
.map(|child| child.layout(view, cx))
|
||||
.collect::<Result<Vec<LayoutId>>>()?;
|
||||
|
||||
cx.add_layout_node(self.style(), (), children)
|
||||
}
|
||||
|
||||
fn paint(&mut self, view: &mut V, layout: &mut Layout<V, ()>, cx: &mut PaintContext<V>)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.style();
|
||||
|
||||
style.paint_background::<V, Self>(layout, cx);
|
||||
for child in &mut self.children {
|
||||
child.paint(view, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Styleable for Div<V> {
|
||||
type Style = Style;
|
||||
|
||||
fn declared_style(&mut self) -> &mut StyleRefinement {
|
||||
&mut self.style
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> StyleHelpers for Div<V> {}
|
||||
|
||||
impl<V> Interactive<V> for Div<V> {
|
||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
|
||||
&mut self.handlers
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> ParentElement<V> for Div<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
// let elt = div().w_auto();
|
||||
}
|
||||
|
||||
// trait Element<V: 'static> {
|
||||
// type Style;
|
||||
|
||||
// fn layout()
|
||||
// }
|
||||
|
||||
// trait Stylable<V: 'static>: Element<V> {
|
||||
// type Style;
|
||||
|
||||
// fn with_style(self, style: Self::Style) -> Self;
|
||||
// }
|
||||
|
||||
// pub struct HoverStyle<S> {
|
||||
// default: S,
|
||||
// hovered: S,
|
||||
// }
|
||||
|
||||
// struct Hover<V: 'static, C: Stylable<V>> {
|
||||
// child: C,
|
||||
// style: HoverStyle<C::Style>,
|
||||
// }
|
||||
|
||||
// impl<V: 'static, C: Stylable<V>> Hover<V, C> {
|
||||
// fn new(child: C, style: HoverStyle<C::Style>) -> Self {
|
||||
// Self { child, style }
|
||||
// }
|
||||
// }
|
158
crates/gpui/playground/src/element.rs
Normal file
158
crates/gpui/playground/src/element.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use gpui::{geometry::rect::RectF, EngineLayout};
|
||||
use smallvec::SmallVec;
|
||||
use std::marker::PhantomData;
|
||||
use util::ResultExt;
|
||||
|
||||
pub use crate::layout_context::LayoutContext;
|
||||
pub use crate::paint_context::PaintContext;
|
||||
|
||||
type LayoutId = gpui::LayoutId;
|
||||
|
||||
pub trait Element<V: 'static>: 'static {
|
||||
type Layout;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
) -> Result<Layout<V, Self::Layout>>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
layout: &mut Layout<V, Self::Layout>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) where
|
||||
Self: Sized;
|
||||
|
||||
fn into_any(self) -> AnyElement<V>
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
AnyElement(Box::new(ElementState {
|
||||
element: self,
|
||||
layout: None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to make ElementState<V, E> into a trait object, so we can wrap it in AnyElement<V>.
|
||||
trait ElementStateObject<V> {
|
||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
|
||||
fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>);
|
||||
}
|
||||
|
||||
/// A wrapper around an element that stores its layout state.
|
||||
struct ElementState<V: 'static, E: Element<V>> {
|
||||
element: E,
|
||||
layout: Option<Layout<V, E::Layout>>,
|
||||
}
|
||||
|
||||
/// We blanket-implement the object-safe ElementStateObject interface to make ElementStates into trait objects
|
||||
impl<V, E: Element<V>> ElementStateObject<V> for ElementState<V, E> {
|
||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
|
||||
let layout = self.element.layout(view, cx)?;
|
||||
let layout_id = layout.id;
|
||||
self.layout = Some(layout);
|
||||
Ok(layout_id)
|
||||
}
|
||||
|
||||
fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
|
||||
let layout = self.layout.as_mut().expect("paint called before layout");
|
||||
if layout.engine_layout.is_none() {
|
||||
layout.engine_layout = cx.computed_layout(layout.id).log_err()
|
||||
}
|
||||
self.element.paint(view, layout, cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// A dynamic element.
|
||||
pub struct AnyElement<V>(Box<dyn ElementStateObject<V>>);
|
||||
|
||||
impl<V> AnyElement<V> {
|
||||
pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
|
||||
self.0.layout(view, cx)
|
||||
}
|
||||
|
||||
pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
|
||||
self.0.paint(view, cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct Layout<V, D> {
|
||||
id: LayoutId,
|
||||
engine_layout: Option<EngineLayout>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
element_data: D,
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static, D> Layout<V, D> {
|
||||
pub fn new(id: LayoutId, element_data: D) -> Self {
|
||||
Self {
|
||||
id,
|
||||
engine_layout: None,
|
||||
element_data: element_data,
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
|
||||
self.engine_layout(cx).bounds
|
||||
}
|
||||
|
||||
pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
|
||||
self.engine_layout(cx).order
|
||||
}
|
||||
|
||||
fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
|
||||
self.engine_layout
|
||||
.get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Layout<V, Option<AnyElement<V>>> {
|
||||
pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) {
|
||||
let mut element = self.element_data.take().unwrap();
|
||||
element.paint(view, cx);
|
||||
self.element_data = Some(element);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ParentElement<V: 'static> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
|
||||
|
||||
fn child(mut self, child: impl IntoElement<V>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.children_mut().push(child.into_element().into_any());
|
||||
self
|
||||
}
|
||||
|
||||
fn children<I, E>(mut self, children: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = E>,
|
||||
E: IntoElement<V>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.children_mut().extend(
|
||||
children
|
||||
.into_iter()
|
||||
.map(|child| child.into_element().into_any()),
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoElement<V: 'static> {
|
||||
type Element: Element<V>;
|
||||
|
||||
fn into_element(self) -> Self::Element;
|
||||
}
|
76
crates/gpui/playground/src/hoverable.rs
Normal file
76
crates/gpui/playground/src/hoverable.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use crate::{
|
||||
element::{Element, Layout},
|
||||
layout_context::LayoutContext,
|
||||
paint_context::PaintContext,
|
||||
style::{StyleRefinement, Styleable},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gpui::platform::MouseMovedEvent;
|
||||
use refineable::Refineable;
|
||||
use std::{cell::Cell, marker::PhantomData};
|
||||
|
||||
pub struct Hoverable<V: 'static, E: Element<V> + Styleable> {
|
||||
hovered: Cell<bool>,
|
||||
child_style: StyleRefinement,
|
||||
hovered_style: StyleRefinement,
|
||||
child: E,
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
|
||||
Hoverable {
|
||||
hovered: Cell::new(false),
|
||||
child_style: child.declared_style().clone(),
|
||||
hovered_style: Default::default(),
|
||||
child,
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
|
||||
type Style = E::Style;
|
||||
|
||||
fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
|
||||
self.child.declared_style()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
|
||||
type Layout = E::Layout;
|
||||
|
||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.child.layout(view, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
layout: &mut Layout<V, Self::Layout>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
if self.hovered.get() {
|
||||
// If hovered, refine the child's style with this element's style.
|
||||
self.child.declared_style().refine(&self.hovered_style);
|
||||
} else {
|
||||
// Otherwise, set the child's style back to its original style.
|
||||
*self.child.declared_style() = self.child_style.clone();
|
||||
}
|
||||
|
||||
let bounds = layout.bounds(cx);
|
||||
let order = layout.order(cx);
|
||||
self.hovered.set(bounds.contains_point(cx.mouse_position()));
|
||||
let was_hovered = self.hovered.clone();
|
||||
cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {
|
||||
let is_hovered = bounds.contains_point(event.position);
|
||||
if is_hovered != was_hovered.get() {
|
||||
was_hovered.set(is_hovered);
|
||||
cx.repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
34
crates/gpui/playground/src/interactive.rs
Normal file
34
crates/gpui/playground/src/interactive.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use gpui::{platform::MouseMovedEvent, EventContext};
|
||||
use smallvec::SmallVec;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait Interactive<V: 'static> {
|
||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
|
||||
|
||||
fn on_mouse_move<H>(mut self, handler: H) -> Self
|
||||
where
|
||||
H: 'static + Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>),
|
||||
Self: Sized,
|
||||
{
|
||||
self.interaction_handlers()
|
||||
.mouse_moved
|
||||
.push(Rc::new(move |view, event, hit_test, cx| {
|
||||
handler(view, event, hit_test, cx);
|
||||
cx.bubble
|
||||
}));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InteractionHandlers<V: 'static> {
|
||||
mouse_moved:
|
||||
SmallVec<[Rc<dyn Fn(&mut V, &MouseMovedEvent, bool, &mut EventContext<V>) -> bool>; 2]>,
|
||||
}
|
||||
|
||||
impl<V> Default for InteractionHandlers<V> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mouse_moved: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
54
crates/gpui/playground/src/layout_context.rs
Normal file
54
crates/gpui/playground/src/layout_context.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub use gpui::LayoutContext as LegacyLayoutContext;
|
||||
use gpui::{RenderContext, ViewContext};
|
||||
pub use taffy::tree::NodeId;
|
||||
|
||||
use crate::{element::Layout, style::Style};
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub(crate) legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, '_, '_, V> {
|
||||
fn text_style(&self) -> gpui::fonts::TextStyle {
|
||||
self.legacy_cx.text_style()
|
||||
}
|
||||
|
||||
fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
|
||||
self.legacy_cx.push_text_style(style)
|
||||
}
|
||||
|
||||
fn pop_text_style(&mut self) {
|
||||
self.legacy_cx.pop_text_style()
|
||||
}
|
||||
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, 'd, V: 'static> LayoutContext<'a, 'b, 'c, 'd, V> {
|
||||
pub fn new(legacy_cx: &'d mut LegacyLayoutContext<'a, 'b, 'c, V>) -> Self {
|
||||
Self { legacy_cx }
|
||||
}
|
||||
|
||||
pub fn add_layout_node<D>(
|
||||
&mut self,
|
||||
style: Style,
|
||||
element_data: D,
|
||||
children: impl IntoIterator<Item = NodeId>,
|
||||
) -> Result<Layout<V, D>> {
|
||||
let rem_size = self.rem_pixels();
|
||||
let id = self
|
||||
.legacy_cx
|
||||
.layout_engine()
|
||||
.ok_or_else(|| anyhow!("no layout engine"))?
|
||||
.add_node(style.to_taffy(rem_size), children)?;
|
||||
|
||||
Ok(Layout::new(id, element_data))
|
||||
}
|
||||
}
|
71
crates/gpui/playground/src/paint_context.rs
Normal file
71
crates/gpui/playground/src/paint_context.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use gpui::{scene::EventHandler, EngineLayout, EventContext, LayoutId, RenderContext, ViewContext};
|
||||
pub use gpui::{LayoutContext, PaintContext as LegacyPaintContext};
|
||||
use std::{any::TypeId, rc::Rc};
|
||||
pub use taffy::tree::NodeId;
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct PaintContext<'a, 'b, 'c, 'd, V> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
pub(crate) legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
|
||||
pub(crate) scene: &'d mut gpui::SceneBuilder,
|
||||
}
|
||||
|
||||
impl<'a, 'b, V> RenderContext<'a, 'b, V> for PaintContext<'a, 'b, '_, '_, V> {
|
||||
fn text_style(&self) -> gpui::fonts::TextStyle {
|
||||
self.legacy_cx.text_style()
|
||||
}
|
||||
|
||||
fn push_text_style(&mut self, style: gpui::fonts::TextStyle) {
|
||||
self.legacy_cx.push_text_style(style)
|
||||
}
|
||||
|
||||
fn pop_text_style(&mut self) {
|
||||
self.legacy_cx.pop_text_style()
|
||||
}
|
||||
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
|
||||
pub fn new(
|
||||
legacy_cx: &'d mut LegacyPaintContext<'a, 'b, 'c, V>,
|
||||
scene: &'d mut gpui::SceneBuilder,
|
||||
) -> Self {
|
||||
Self { legacy_cx, scene }
|
||||
}
|
||||
|
||||
pub fn on_event<E: 'static>(
|
||||
&mut self,
|
||||
order: u32,
|
||||
handler: impl Fn(&mut V, &E, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
let view = self.weak_handle();
|
||||
|
||||
self.scene.event_handlers.push(EventHandler {
|
||||
order,
|
||||
handler: Rc::new(move |event, window_cx| {
|
||||
if let Some(view) = view.upgrade(window_cx) {
|
||||
view.update(window_cx, |view, view_cx| {
|
||||
let mut event_cx = EventContext::new(view_cx);
|
||||
handler(view, event.downcast_ref().unwrap(), &mut event_cx);
|
||||
event_cx.bubble
|
||||
})
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}),
|
||||
event_type: TypeId::of::<E>(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn computed_layout(&mut self, layout_id: LayoutId) -> Result<EngineLayout> {
|
||||
self.layout_engine()
|
||||
.ok_or_else(|| anyhow!("no layout engine present"))?
|
||||
.computed_layout(layout_id)
|
||||
}
|
||||
}
|
83
crates/gpui/playground/src/playground.rs
Normal file
83
crates/gpui/playground/src/playground.rs
Normal file
@ -0,0 +1,83 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
use crate::{color::black, style::StyleHelpers};
|
||||
use element::Element;
|
||||
use gpui::{
|
||||
geometry::{rect::RectF, vector::vec2f},
|
||||
platform::WindowOptions,
|
||||
};
|
||||
use log::LevelFilter;
|
||||
use simplelog::SimpleLogger;
|
||||
use themes::{rose_pine, ThemeColors};
|
||||
use view::view;
|
||||
|
||||
mod adapter;
|
||||
mod color;
|
||||
mod components;
|
||||
mod div;
|
||||
mod element;
|
||||
mod hoverable;
|
||||
mod interactive;
|
||||
mod layout_context;
|
||||
mod paint_context;
|
||||
mod style;
|
||||
mod text;
|
||||
mod themes;
|
||||
mod view;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||
|
||||
gpui::App::new(()).unwrap().run(|cx| {
|
||||
cx.add_window(
|
||||
WindowOptions {
|
||||
bounds: gpui::platform::WindowBounds::Fixed(RectF::new(
|
||||
vec2f(0., 0.),
|
||||
vec2f(400., 300.),
|
||||
)),
|
||||
center: true,
|
||||
..Default::default()
|
||||
},
|
||||
|_| view(|_| playground(&rose_pine::moon())),
|
||||
);
|
||||
cx.platform().activate(true);
|
||||
});
|
||||
}
|
||||
|
||||
fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
use div::div;
|
||||
|
||||
div()
|
||||
.text_color(black())
|
||||
.h_full()
|
||||
.w_1_2()
|
||||
.fill(theme.success(0.5))
|
||||
// .hover()
|
||||
// .fill(theme.error(0.5))
|
||||
// .child(button().label("Hello").click(|_, _, _| println!("click!")))
|
||||
}
|
||||
|
||||
// todo!()
|
||||
// // column()
|
||||
// // .size(auto())
|
||||
// // .fill(theme.base(0.5))
|
||||
// // .text_color(theme.text(0.5))
|
||||
// // .child(title_bar(theme))
|
||||
// // .child(stage(theme))
|
||||
// // .child(status_bar(theme))
|
||||
// }
|
||||
|
||||
// fn title_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
// row()
|
||||
// .fill(theme.base(0.2))
|
||||
// .justify(0.)
|
||||
// .width(auto())
|
||||
// .child(text("Zed Playground"))
|
||||
// }
|
||||
|
||||
// fn stage<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
// row().fill(theme.surface(0.9))
|
||||
// }
|
||||
|
||||
// fn status_bar<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
|
||||
// row().fill(theme.surface(0.1))
|
||||
// }
|
286
crates/gpui/playground/src/style.rs
Normal file
286
crates/gpui/playground/src/style.rs
Normal file
@ -0,0 +1,286 @@
|
||||
use crate::{
|
||||
color::Hsla,
|
||||
element::{Element, Layout},
|
||||
paint_context::PaintContext,
|
||||
};
|
||||
use gpui::{
|
||||
fonts::TextStyleRefinement,
|
||||
geometry::{
|
||||
AbsoluteLength, DefiniteLength, Edges, EdgesRefinement, Length, Point, PointRefinement,
|
||||
Size, SizeRefinement,
|
||||
},
|
||||
};
|
||||
use playground_macros::styleable_helpers;
|
||||
use refineable::Refineable;
|
||||
pub use taffy::style::{
|
||||
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
||||
Overflow, Position,
|
||||
};
|
||||
|
||||
#[derive(Clone, Refineable)]
|
||||
pub struct Style {
|
||||
/// What layout strategy should be used?
|
||||
pub display: Display,
|
||||
|
||||
// Overflow properties
|
||||
/// How children overflowing their container should affect layout
|
||||
#[refineable]
|
||||
pub overflow: Point<Overflow>,
|
||||
/// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
|
||||
pub scrollbar_width: f32,
|
||||
|
||||
// Position properties
|
||||
/// What should the `position` value of this struct use as a base offset?
|
||||
pub position: Position,
|
||||
/// How should the position of this element be tweaked relative to the layout defined?
|
||||
#[refineable]
|
||||
pub inset: Edges<Length>,
|
||||
|
||||
// Size properies
|
||||
/// Sets the initial size of the item
|
||||
#[refineable]
|
||||
pub size: Size<Length>,
|
||||
/// Controls the minimum size of the item
|
||||
#[refineable]
|
||||
pub min_size: Size<Length>,
|
||||
/// Controls the maximum size of the item
|
||||
#[refineable]
|
||||
pub max_size: Size<Length>,
|
||||
/// Sets the preferred aspect ratio for the item. The ratio is calculated as width divided by height.
|
||||
pub aspect_ratio: Option<f32>,
|
||||
|
||||
// Spacing Properties
|
||||
/// How large should the margin be on each side?
|
||||
#[refineable]
|
||||
pub margin: Edges<Length>,
|
||||
/// How large should the padding be on each side?
|
||||
#[refineable]
|
||||
pub padding: Edges<DefiniteLength>,
|
||||
/// How large should the border be on each side?
|
||||
#[refineable]
|
||||
pub border: Edges<DefiniteLength>,
|
||||
|
||||
// Alignment properties
|
||||
/// How this node's children aligned in the cross/block axis?
|
||||
pub align_items: Option<AlignItems>,
|
||||
/// How this node should be aligned in the cross/block axis. Falls back to the parents [`AlignItems`] if not set
|
||||
pub align_self: Option<AlignSelf>,
|
||||
/// How should content contained within this item be aligned in the cross/block axis
|
||||
pub align_content: Option<AlignContent>,
|
||||
/// How should contained within this item be aligned in the main/inline axis
|
||||
pub justify_content: Option<JustifyContent>,
|
||||
/// How large should the gaps between items in a flex container be?
|
||||
#[refineable]
|
||||
pub gap: Size<DefiniteLength>,
|
||||
|
||||
// Flexbox properies
|
||||
/// Which direction does the main axis flow in?
|
||||
pub flex_direction: FlexDirection,
|
||||
/// Should elements wrap, or stay in a single line?
|
||||
pub flex_wrap: FlexWrap,
|
||||
/// Sets the initial main axis size of the item
|
||||
pub flex_basis: Length,
|
||||
/// The relative rate at which this item grows when it is expanding to fill space, 0.0 is the default value, and this value must be positive.
|
||||
pub flex_grow: f32,
|
||||
/// The relative rate at which this item shrinks when it is contracting to fit into space, 1.0 is the default value, and this value must be positive.
|
||||
pub flex_shrink: f32,
|
||||
|
||||
/// The fill color of this element
|
||||
pub fill: Option<Fill>,
|
||||
/// The radius of the corners of this element
|
||||
#[refineable]
|
||||
pub corner_radii: CornerRadii,
|
||||
/// The color of text within this element. Cascades to children unless overridden.
|
||||
pub text_color: Option<Hsla>,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::style::Style {
|
||||
taffy::style::Style {
|
||||
display: self.display,
|
||||
overflow: self.overflow.clone().into(),
|
||||
scrollbar_width: self.scrollbar_width,
|
||||
position: self.position,
|
||||
inset: self.inset.to_taffy(rem_size),
|
||||
size: self.size.to_taffy(rem_size),
|
||||
min_size: self.min_size.to_taffy(rem_size),
|
||||
max_size: self.max_size.to_taffy(rem_size),
|
||||
aspect_ratio: self.aspect_ratio,
|
||||
margin: self.margin.to_taffy(rem_size),
|
||||
padding: self.padding.to_taffy(rem_size),
|
||||
border: self.border.to_taffy(rem_size),
|
||||
align_items: self.align_items,
|
||||
align_self: self.align_self,
|
||||
align_content: self.align_content,
|
||||
justify_content: self.justify_content,
|
||||
gap: self.gap.to_taffy(rem_size),
|
||||
flex_direction: self.flex_direction,
|
||||
flex_wrap: self.flex_wrap,
|
||||
flex_basis: self.flex_basis.to_taffy(rem_size).into(),
|
||||
flex_grow: self.flex_grow,
|
||||
flex_shrink: self.flex_shrink,
|
||||
..Default::default() // Ignore grid properties for now
|
||||
}
|
||||
}
|
||||
|
||||
/// Paints the background of an element styled with this style.
|
||||
/// Return the bounds in which to paint the content.
|
||||
pub fn paint_background<V: 'static, E: Element<V>>(
|
||||
&self,
|
||||
layout: &mut Layout<V, E::Layout>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
let bounds = layout.bounds(cx);
|
||||
let rem_size = cx.rem_pixels();
|
||||
if let Some(color) = self.fill.as_ref().and_then(Fill::color) {
|
||||
cx.scene.push_quad(gpui::Quad {
|
||||
bounds,
|
||||
background: Some(color.into()),
|
||||
corner_radii: self.corner_radii.to_gpui(rem_size),
|
||||
border: Default::default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Style {
|
||||
fn default() -> Self {
|
||||
Style {
|
||||
display: Display::DEFAULT,
|
||||
overflow: Point {
|
||||
x: Overflow::Visible,
|
||||
y: Overflow::Visible,
|
||||
},
|
||||
scrollbar_width: 0.0,
|
||||
position: Position::Relative,
|
||||
inset: Edges::auto(),
|
||||
margin: Edges::<Length>::zero(),
|
||||
padding: Edges::<DefiniteLength>::zero(),
|
||||
border: Edges::<DefiniteLength>::zero(),
|
||||
size: Size::auto(),
|
||||
min_size: Size::auto(),
|
||||
max_size: Size::auto(),
|
||||
aspect_ratio: None,
|
||||
gap: Size::zero(),
|
||||
// Aligment
|
||||
align_items: None,
|
||||
align_self: None,
|
||||
align_content: None,
|
||||
justify_content: None,
|
||||
// Flexbox
|
||||
flex_direction: FlexDirection::Row,
|
||||
flex_wrap: FlexWrap::NoWrap,
|
||||
flex_grow: 0.0,
|
||||
flex_shrink: 1.0,
|
||||
flex_basis: Length::Auto,
|
||||
fill: None,
|
||||
text_color: None,
|
||||
corner_radii: CornerRadii::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleRefinement {
|
||||
pub fn text_style(&self) -> Option<TextStyleRefinement> {
|
||||
self.text_color.map(|color| TextStyleRefinement {
|
||||
color: Some(color.into()),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OptionalTextStyle {
|
||||
color: Option<Hsla>,
|
||||
}
|
||||
|
||||
impl OptionalTextStyle {
|
||||
pub fn apply(&self, style: &mut gpui::fonts::TextStyle) {
|
||||
if let Some(color) = self.color {
|
||||
style.color = color.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Fill {
|
||||
Color(Hsla),
|
||||
}
|
||||
|
||||
impl Fill {
|
||||
pub fn color(&self) -> Option<Hsla> {
|
||||
match self {
|
||||
Fill::Color(color) => Some(*color),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Fill {
|
||||
fn default() -> Self {
|
||||
Self::Color(Hsla::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hsla> for Fill {
|
||||
fn from(color: Hsla) -> Self {
|
||||
Self::Color(color)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Refineable, Default)]
|
||||
pub struct CornerRadii {
|
||||
top_left: AbsoluteLength,
|
||||
top_right: AbsoluteLength,
|
||||
bottom_left: AbsoluteLength,
|
||||
bottom_right: AbsoluteLength,
|
||||
}
|
||||
|
||||
impl CornerRadii {
|
||||
pub fn to_gpui(&self, rem_size: f32) -> gpui::scene::CornerRadii {
|
||||
gpui::scene::CornerRadii {
|
||||
top_left: self.top_left.to_pixels(rem_size),
|
||||
top_right: self.top_right.to_pixels(rem_size),
|
||||
bottom_left: self.bottom_left.to_pixels(rem_size),
|
||||
bottom_right: self.bottom_right.to_pixels(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Styleable {
|
||||
type Style: refineable::Refineable;
|
||||
|
||||
fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
|
||||
|
||||
fn style(&mut self) -> playground::style::Style {
|
||||
let mut style = playground::style::Style::default();
|
||||
style.refine(self.declared_style());
|
||||
style
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers methods that take and return mut self. This includes tailwind style methods for standard sizes etc.
|
||||
//
|
||||
// Example:
|
||||
// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
|
||||
// fn p_2(mut self) -> Self where Self: Sized;
|
||||
use crate as playground; // Macro invocation references this crate as playground.
|
||||
pub trait StyleHelpers: Styleable<Style = Style> {
|
||||
styleable_helpers!();
|
||||
|
||||
fn fill<F>(mut self, fill: F) -> Self
|
||||
where
|
||||
F: Into<Fill>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.declared_style().fill = Some(fill.into());
|
||||
self
|
||||
}
|
||||
|
||||
fn text_color<C>(mut self, color: C) -> Self
|
||||
where
|
||||
C: Into<Hsla>,
|
||||
Self: Sized,
|
||||
{
|
||||
self.declared_style().text_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
}
|
151
crates/gpui/playground/src/text.rs
Normal file
151
crates/gpui/playground/src/text.rs
Normal file
@ -0,0 +1,151 @@
|
||||
use crate::{
|
||||
element::{Element, IntoElement, Layout},
|
||||
layout_context::LayoutContext,
|
||||
paint_context::PaintContext,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gpui::text_layout::LineLayout;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
|
||||
type Element = Text;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
Text { text: self.into() }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Text {
|
||||
text: ArcCow<'static, str>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for Text {
|
||||
type Layout = Arc<Mutex<Option<TextLayout>>>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
) -> Result<Layout<V, Self::Layout>> {
|
||||
// let rem_size = cx.rem_pixels();
|
||||
// let fonts = cx.platform().fonts();
|
||||
// let text_style = cx.text_style();
|
||||
// let line_height = cx.font_cache().line_height(text_style.font_size);
|
||||
// let layout_engine = cx.layout_engine().expect("no layout engine present");
|
||||
// let text = self.text.clone();
|
||||
// let layout = Arc::new(Mutex::new(None));
|
||||
|
||||
// let style: Style = Style::default().refined(&self.metadata.style);
|
||||
// let node_id = layout_engine.add_measured_node(style.to_taffy(rem_size), {
|
||||
// let layout = layout.clone();
|
||||
// move |params| {
|
||||
// let line_layout = fonts.layout_line(
|
||||
// text.as_ref(),
|
||||
// text_style.font_size,
|
||||
// &[(text.len(), text_style.to_run())],
|
||||
// );
|
||||
|
||||
// let size = Size {
|
||||
// width: line_layout.width,
|
||||
// height: line_height,
|
||||
// };
|
||||
|
||||
// layout.lock().replace(TextLayout {
|
||||
// line_layout: Arc::new(line_layout),
|
||||
// line_height,
|
||||
// });
|
||||
|
||||
// size
|
||||
// }
|
||||
// })?;
|
||||
|
||||
// Ok((node_id, layout))
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn paint<'a>(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
layout: &mut Layout<V, Self::Layout>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
// ) {
|
||||
// let element_layout_lock = layout.from_element.lock();
|
||||
// let element_layout = element_layout_lock
|
||||
// .as_ref()
|
||||
// .expect("layout has not been performed");
|
||||
// let line_layout = element_layout.line_layout.clone();
|
||||
// let line_height = element_layout.line_height;
|
||||
// drop(element_layout_lock);
|
||||
|
||||
// let text_style = cx.text_style();
|
||||
// let line =
|
||||
// gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
|
||||
// line.paint(
|
||||
// cx.scene,
|
||||
// layout.from_engine.bounds.origin(),
|
||||
// layout.from_engine.bounds,
|
||||
// line_height,
|
||||
// cx.legacy_cx,
|
||||
// );
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextLayout {
|
||||
line_layout: Arc<LineLayout>,
|
||||
line_height: f32,
|
||||
}
|
||||
|
||||
pub enum ArcCow<'a, T: ?Sized> {
|
||||
Borrowed(&'a T),
|
||||
Owned(Arc<T>),
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Clone for ArcCow<'a, T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Borrowed(borrowed) => Self::Borrowed(borrowed),
|
||||
Self::Owned(owned) => Self::Owned(owned.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> From<&'a T> for ArcCow<'a, T> {
|
||||
fn from(s: &'a T) -> Self {
|
||||
Self::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Arc<T>> for ArcCow<'_, T> {
|
||||
fn from(s: Arc<T>) -> Self {
|
||||
Self::Owned(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ArcCow<'_, str> {
|
||||
fn from(value: String) -> Self {
|
||||
Self::Owned(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> std::ops::Deref for ArcCow<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
ArcCow::Borrowed(s) => s,
|
||||
ArcCow::Owned(s) => s.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> AsRef<T> for ArcCow<'_, T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
match self {
|
||||
ArcCow::Borrowed(borrowed) => borrowed,
|
||||
ArcCow::Owned(owned) => owned.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
84
crates/gpui/playground/src/themes.rs
Normal file
84
crates/gpui/playground/src/themes.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use crate::color::{Hsla, Lerp};
|
||||
use std::ops::Range;
|
||||
|
||||
pub mod rose_pine;
|
||||
|
||||
pub struct ThemeColors {
|
||||
pub base: Range<Hsla>,
|
||||
pub surface: Range<Hsla>,
|
||||
pub overlay: Range<Hsla>,
|
||||
pub muted: Range<Hsla>,
|
||||
pub subtle: Range<Hsla>,
|
||||
pub text: Range<Hsla>,
|
||||
pub highlight_low: Range<Hsla>,
|
||||
pub highlight_med: Range<Hsla>,
|
||||
pub highlight_high: Range<Hsla>,
|
||||
pub success: Range<Hsla>,
|
||||
pub warning: Range<Hsla>,
|
||||
pub error: Range<Hsla>,
|
||||
pub inserted: Range<Hsla>,
|
||||
pub deleted: Range<Hsla>,
|
||||
pub modified: Range<Hsla>,
|
||||
}
|
||||
|
||||
impl ThemeColors {
|
||||
pub fn base(&self, level: f32) -> Hsla {
|
||||
self.base.lerp(level)
|
||||
}
|
||||
|
||||
pub fn surface(&self, level: f32) -> Hsla {
|
||||
self.surface.lerp(level)
|
||||
}
|
||||
|
||||
pub fn overlay(&self, level: f32) -> Hsla {
|
||||
self.overlay.lerp(level)
|
||||
}
|
||||
|
||||
pub fn muted(&self, level: f32) -> Hsla {
|
||||
self.muted.lerp(level)
|
||||
}
|
||||
|
||||
pub fn subtle(&self, level: f32) -> Hsla {
|
||||
self.subtle.lerp(level)
|
||||
}
|
||||
|
||||
pub fn text(&self, level: f32) -> Hsla {
|
||||
self.text.lerp(level)
|
||||
}
|
||||
|
||||
pub fn highlight_low(&self, level: f32) -> Hsla {
|
||||
self.highlight_low.lerp(level)
|
||||
}
|
||||
|
||||
pub fn highlight_med(&self, level: f32) -> Hsla {
|
||||
self.highlight_med.lerp(level)
|
||||
}
|
||||
|
||||
pub fn highlight_high(&self, level: f32) -> Hsla {
|
||||
self.highlight_high.lerp(level)
|
||||
}
|
||||
|
||||
pub fn success(&self, level: f32) -> Hsla {
|
||||
self.success.lerp(level)
|
||||
}
|
||||
|
||||
pub fn warning(&self, level: f32) -> Hsla {
|
||||
self.warning.lerp(level)
|
||||
}
|
||||
|
||||
pub fn error(&self, level: f32) -> Hsla {
|
||||
self.error.lerp(level)
|
||||
}
|
||||
|
||||
pub fn inserted(&self, level: f32) -> Hsla {
|
||||
self.inserted.lerp(level)
|
||||
}
|
||||
|
||||
pub fn deleted(&self, level: f32) -> Hsla {
|
||||
self.deleted.lerp(level)
|
||||
}
|
||||
|
||||
pub fn modified(&self, level: f32) -> Hsla {
|
||||
self.modified.lerp(level)
|
||||
}
|
||||
}
|
133
crates/gpui/playground/src/themes/rose_pine.rs
Normal file
133
crates/gpui/playground/src/themes/rose_pine.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
color::{hsla, rgb, Hsla},
|
||||
ThemeColors,
|
||||
};
|
||||
|
||||
pub struct RosePineThemes {
|
||||
pub default: RosePinePalette,
|
||||
pub dawn: RosePinePalette,
|
||||
pub moon: RosePinePalette,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RosePinePalette {
|
||||
pub base: Hsla,
|
||||
pub surface: Hsla,
|
||||
pub overlay: Hsla,
|
||||
pub muted: Hsla,
|
||||
pub subtle: Hsla,
|
||||
pub text: Hsla,
|
||||
pub love: Hsla,
|
||||
pub gold: Hsla,
|
||||
pub rose: Hsla,
|
||||
pub pine: Hsla,
|
||||
pub foam: Hsla,
|
||||
pub iris: Hsla,
|
||||
pub highlight_low: Hsla,
|
||||
pub highlight_med: Hsla,
|
||||
pub highlight_high: Hsla,
|
||||
}
|
||||
|
||||
impl RosePinePalette {
|
||||
pub fn default() -> RosePinePalette {
|
||||
RosePinePalette {
|
||||
base: rgb(0x191724),
|
||||
surface: rgb(0x1f1d2e),
|
||||
overlay: rgb(0x26233a),
|
||||
muted: rgb(0x6e6a86),
|
||||
subtle: rgb(0x908caa),
|
||||
text: rgb(0xe0def4),
|
||||
love: rgb(0xeb6f92),
|
||||
gold: rgb(0xf6c177),
|
||||
rose: rgb(0xebbcba),
|
||||
pine: rgb(0x31748f),
|
||||
foam: rgb(0x9ccfd8),
|
||||
iris: rgb(0xc4a7e7),
|
||||
highlight_low: rgb(0x21202e),
|
||||
highlight_med: rgb(0x403d52),
|
||||
highlight_high: rgb(0x524f67),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn moon() -> RosePinePalette {
|
||||
RosePinePalette {
|
||||
base: rgb(0x232136),
|
||||
surface: rgb(0x2a273f),
|
||||
overlay: rgb(0x393552),
|
||||
muted: rgb(0x6e6a86),
|
||||
subtle: rgb(0x908caa),
|
||||
text: rgb(0xe0def4),
|
||||
love: rgb(0xeb6f92),
|
||||
gold: rgb(0xf6c177),
|
||||
rose: rgb(0xea9a97),
|
||||
pine: rgb(0x3e8fb0),
|
||||
foam: rgb(0x9ccfd8),
|
||||
iris: rgb(0xc4a7e7),
|
||||
highlight_low: rgb(0x2a283e),
|
||||
highlight_med: rgb(0x44415a),
|
||||
highlight_high: rgb(0x56526e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dawn() -> RosePinePalette {
|
||||
RosePinePalette {
|
||||
base: rgb(0xfaf4ed),
|
||||
surface: rgb(0xfffaf3),
|
||||
overlay: rgb(0xf2e9e1),
|
||||
muted: rgb(0x9893a5),
|
||||
subtle: rgb(0x797593),
|
||||
text: rgb(0x575279),
|
||||
love: rgb(0xb4637a),
|
||||
gold: rgb(0xea9d34),
|
||||
rose: rgb(0xd7827e),
|
||||
pine: rgb(0x286983),
|
||||
foam: rgb(0x56949f),
|
||||
iris: rgb(0x907aa9),
|
||||
highlight_low: rgb(0xf4ede8),
|
||||
highlight_med: rgb(0xdfdad9),
|
||||
highlight_high: rgb(0xcecacd),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default() -> ThemeColors {
|
||||
theme_colors(&RosePinePalette::default())
|
||||
}
|
||||
|
||||
pub fn moon() -> ThemeColors {
|
||||
theme_colors(&RosePinePalette::moon())
|
||||
}
|
||||
|
||||
pub fn dawn() -> ThemeColors {
|
||||
theme_colors(&RosePinePalette::dawn())
|
||||
}
|
||||
|
||||
fn theme_colors(p: &RosePinePalette) -> ThemeColors {
|
||||
ThemeColors {
|
||||
base: scale_sl(p.base, (0.8, 0.8), (1.2, 1.2)),
|
||||
surface: scale_sl(p.surface, (0.8, 0.8), (1.2, 1.2)),
|
||||
overlay: scale_sl(p.overlay, (0.8, 0.8), (1.2, 1.2)),
|
||||
muted: scale_sl(p.muted, (0.8, 0.8), (1.2, 1.2)),
|
||||
subtle: scale_sl(p.subtle, (0.8, 0.8), (1.2, 1.2)),
|
||||
text: scale_sl(p.text, (0.8, 0.8), (1.2, 1.2)),
|
||||
highlight_low: scale_sl(p.highlight_low, (0.8, 0.8), (1.2, 1.2)),
|
||||
highlight_med: scale_sl(p.highlight_med, (0.8, 0.8), (1.2, 1.2)),
|
||||
highlight_high: scale_sl(p.highlight_high, (0.8, 0.8), (1.2, 1.2)),
|
||||
success: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
|
||||
warning: scale_sl(p.gold, (0.8, 0.8), (1.2, 1.2)),
|
||||
error: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
|
||||
inserted: scale_sl(p.foam, (0.8, 0.8), (1.2, 1.2)),
|
||||
deleted: scale_sl(p.love, (0.8, 0.8), (1.2, 1.2)),
|
||||
modified: scale_sl(p.rose, (0.8, 0.8), (1.2, 1.2)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces a range by multiplying the saturation and lightness of the base color by the given
|
||||
/// start and end factors.
|
||||
fn scale_sl(base: Hsla, (start_s, start_l): (f32, f32), (end_s, end_l): (f32, f32)) -> Range<Hsla> {
|
||||
let start = hsla(base.h, base.s * start_s, base.l * start_l, base.a);
|
||||
let end = hsla(base.h, base.s * end_s, base.l * end_l, base.a);
|
||||
Range { start, end }
|
||||
}
|
26
crates/gpui/playground/src/view.rs
Normal file
26
crates/gpui/playground/src/view.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use crate::{
|
||||
adapter::AdapterElement,
|
||||
element::{AnyElement, Element},
|
||||
};
|
||||
use gpui::ViewContext;
|
||||
|
||||
pub fn view<F, E>(mut render: F) -> ViewFn
|
||||
where
|
||||
F: 'static + FnMut(&mut ViewContext<ViewFn>) -> E,
|
||||
E: Element<ViewFn>,
|
||||
{
|
||||
ViewFn(Box::new(move |cx| (render)(cx).into_any()))
|
||||
}
|
||||
|
||||
pub struct ViewFn(Box<dyn FnMut(&mut ViewContext<ViewFn>) -> AnyElement<ViewFn>>);
|
||||
|
||||
impl gpui::Entity for ViewFn {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl gpui::View for ViewFn {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> gpui::AnyElement<Self> {
|
||||
use gpui::Element as _;
|
||||
AdapterElement((self.0)(cx)).into_any()
|
||||
}
|
||||
}
|
14
crates/gpui/playground_macros/Cargo.toml
Normal file
14
crates/gpui/playground_macros/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "playground_macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
path = "src/playground_macros.rs"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "1.0.72"
|
||||
quote = "1.0.9"
|
||||
proc-macro2 = "1.0.66"
|
91
crates/gpui/playground_macros/src/derive_element.rs
Normal file
91
crates/gpui/playground_macros/src/derive_element.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Ident;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics};
|
||||
|
||||
use crate::derive_into_element::impl_into_element;
|
||||
|
||||
pub fn derive_element(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let type_name = ast.ident;
|
||||
let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
|
||||
|
||||
let (impl_generics, type_generics, where_clause, view_type_name, lifetimes) =
|
||||
if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
|
||||
if let GenericParam::Type(type_param) = param {
|
||||
Some(type_param.ident.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
let mut lifetimes = vec![];
|
||||
for param in ast.generics.params.iter() {
|
||||
if let GenericParam::Lifetime(lifetime_def) = param {
|
||||
lifetimes.push(lifetime_def.lifetime.clone());
|
||||
}
|
||||
}
|
||||
let generics = ast.generics.split_for_impl();
|
||||
(
|
||||
generics.0,
|
||||
Some(generics.1),
|
||||
generics.2,
|
||||
first_type_param,
|
||||
lifetimes,
|
||||
)
|
||||
} else {
|
||||
let generics = placeholder_view_generics.split_for_impl();
|
||||
let placeholder_view_type_name: Ident = parse_quote! { V };
|
||||
(
|
||||
generics.0,
|
||||
None,
|
||||
generics.2,
|
||||
placeholder_view_type_name,
|
||||
vec![],
|
||||
)
|
||||
};
|
||||
|
||||
let lifetimes = if !lifetimes.is_empty() {
|
||||
quote! { <#(#lifetimes),*> }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
let impl_into_element = impl_into_element(
|
||||
&impl_generics,
|
||||
&view_type_name,
|
||||
&type_name,
|
||||
&type_generics,
|
||||
&where_clause,
|
||||
);
|
||||
|
||||
let gen = quote! {
|
||||
impl #impl_generics playground::element::Element<#view_type_name> for #type_name #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Layout = Option<playground::element::AnyElement<#view_type_name #lifetimes>>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
cx: &mut playground::element::LayoutContext<V>,
|
||||
) -> anyhow::Result<playground::element::Layout<V, Self::Layout>> {
|
||||
let mut element = self.render(view, cx).into_any();
|
||||
let layout_id = element.layout(view, cx)?;
|
||||
Ok(playground::element::Layout::new(layout_id, Some(element)))
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
layout: &mut playground::element::Layout<V, Self::Layout>,
|
||||
cx: &mut playground::element::PaintContext<V>,
|
||||
) {
|
||||
layout.paint(view, cx);
|
||||
}
|
||||
}
|
||||
|
||||
#impl_into_element
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
69
crates/gpui/playground_macros/src/derive_into_element.rs
Normal file
69
crates/gpui/playground_macros/src/derive_into_element.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, DeriveInput, GenericParam, Generics, Ident, WhereClause,
|
||||
};
|
||||
|
||||
pub fn derive_into_element(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let type_name = ast.ident;
|
||||
|
||||
let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
|
||||
let placeholder_view_type_name: Ident = parse_quote! { V };
|
||||
let view_type_name: Ident;
|
||||
let impl_generics: syn::ImplGenerics<'_>;
|
||||
let type_generics: Option<syn::TypeGenerics<'_>>;
|
||||
let where_clause: Option<&'_ WhereClause>;
|
||||
|
||||
match ast.generics.params.iter().find_map(|param| {
|
||||
if let GenericParam::Type(type_param) = param {
|
||||
Some(type_param.ident.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(type_name) => {
|
||||
view_type_name = type_name;
|
||||
let generics = ast.generics.split_for_impl();
|
||||
impl_generics = generics.0;
|
||||
type_generics = Some(generics.1);
|
||||
where_clause = generics.2;
|
||||
}
|
||||
_ => {
|
||||
view_type_name = placeholder_view_type_name;
|
||||
let generics = placeholder_view_generics.split_for_impl();
|
||||
impl_generics = generics.0;
|
||||
type_generics = None;
|
||||
where_clause = generics.2;
|
||||
}
|
||||
}
|
||||
|
||||
impl_into_element(
|
||||
&impl_generics,
|
||||
&view_type_name,
|
||||
&type_name,
|
||||
&type_generics,
|
||||
&where_clause,
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn impl_into_element(
|
||||
impl_generics: &syn::ImplGenerics<'_>,
|
||||
view_type_name: &Ident,
|
||||
type_name: &Ident,
|
||||
type_generics: &Option<syn::TypeGenerics<'_>>,
|
||||
where_clause: &Option<&WhereClause>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
quote! {
|
||||
impl #impl_generics playground::element::IntoElement<#view_type_name> for #type_name #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
crates/gpui/playground_macros/src/playground_macros.rs
Normal file
26
crates/gpui/playground_macros/src/playground_macros.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
mod derive_element;
|
||||
mod derive_into_element;
|
||||
mod styleable_helpers;
|
||||
mod tailwind_lengths;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn styleable_helpers(args: TokenStream) -> TokenStream {
|
||||
styleable_helpers::styleable_helpers(args)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Element, attributes(element_crate))]
|
||||
pub fn derive_element(input: TokenStream) -> TokenStream {
|
||||
derive_element::derive_element(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(IntoElement, attributes(element_crate))]
|
||||
pub fn derive_into_element(input: TokenStream) -> TokenStream {
|
||||
derive_into_element::derive_into_element(input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn tailwind_lengths(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
tailwind_lengths::tailwind_lengths(attr, item)
|
||||
}
|
147
crates/gpui/playground_macros/src/styleable_helpers.rs
Normal file
147
crates/gpui/playground_macros/src/styleable_helpers.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream, Result},
|
||||
parse_macro_input,
|
||||
};
|
||||
|
||||
struct StyleableMacroInput;
|
||||
|
||||
impl Parse for StyleableMacroInput {
|
||||
fn parse(_input: ParseStream) -> Result<Self> {
|
||||
Ok(StyleableMacroInput)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn styleable_helpers(input: TokenStream) -> TokenStream {
|
||||
let _ = parse_macro_input!(input as StyleableMacroInput);
|
||||
let methods = generate_methods();
|
||||
let output = quote! {
|
||||
#(#methods)*
|
||||
};
|
||||
output.into()
|
||||
}
|
||||
|
||||
fn generate_methods() -> Vec<TokenStream2> {
|
||||
let mut methods = Vec::new();
|
||||
|
||||
for (prefix, auto_allowed, fields) in tailwind_prefixes() {
|
||||
for (suffix, length_tokens) in tailwind_lengths() {
|
||||
if !auto_allowed && suffix == "auto" {
|
||||
// Conditional to skip "auto"
|
||||
continue;
|
||||
}
|
||||
|
||||
let method_name = format_ident!("{}_{}", prefix, suffix);
|
||||
let field_assignments = fields
|
||||
.iter()
|
||||
.map(|field_tokens| {
|
||||
quote! {
|
||||
style.#field_tokens = Some(gpui::geometry::#length_tokens);
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let method = quote! {
|
||||
fn #method_name(mut self) -> Self where Self: std::marker::Sized {
|
||||
let mut style = self.declared_style();
|
||||
#(#field_assignments)*
|
||||
self
|
||||
}
|
||||
};
|
||||
|
||||
methods.push(method);
|
||||
}
|
||||
}
|
||||
|
||||
methods
|
||||
}
|
||||
|
||||
fn tailwind_lengths() -> Vec<(&'static str, TokenStream2)> {
|
||||
vec![
|
||||
("0", quote! { pixels(0.) }),
|
||||
("1", quote! { rems(0.25) }),
|
||||
("2", quote! { rems(0.5) }),
|
||||
("3", quote! { rems(0.75) }),
|
||||
("4", quote! { rems(1.) }),
|
||||
("5", quote! { rems(1.25) }),
|
||||
("6", quote! { rems(1.5) }),
|
||||
("8", quote! { rems(2.0) }),
|
||||
("10", quote! { rems(2.5) }),
|
||||
("12", quote! { rems(3.) }),
|
||||
("16", quote! { rems(4.) }),
|
||||
("20", quote! { rems(5.) }),
|
||||
("24", quote! { rems(6.) }),
|
||||
("32", quote! { rems(8.) }),
|
||||
("40", quote! { rems(10.) }),
|
||||
("48", quote! { rems(12.) }),
|
||||
("56", quote! { rems(14.) }),
|
||||
("64", quote! { rems(16.) }),
|
||||
("72", quote! { rems(18.) }),
|
||||
("80", quote! { rems(20.) }),
|
||||
("96", quote! { rems(24.) }),
|
||||
("auto", quote! { auto() }),
|
||||
("px", quote! { pixels(1.) }),
|
||||
("full", quote! { relative(1.) }),
|
||||
("1_2", quote! { relative(0.5) }),
|
||||
("1_3", quote! { relative(1./3.) }),
|
||||
("2_3", quote! { relative(2./3.) }),
|
||||
("1_4", quote! { relative(0.25) }),
|
||||
("2_4", quote! { relative(0.5) }),
|
||||
("3_4", quote! { relative(0.75) }),
|
||||
("1_5", quote! { relative(0.2) }),
|
||||
("2_5", quote! { relative(0.4) }),
|
||||
("3_5", quote! { relative(0.6) }),
|
||||
("4_5", quote! { relative(0.8) }),
|
||||
("1_6", quote! { relative(1./6.) }),
|
||||
("5_6", quote! { relative(5./6.) }),
|
||||
("1_12", quote! { relative(1./12.) }),
|
||||
// ("screen_50", quote! { DefiniteLength::Vh(50.0) }),
|
||||
// ("screen_75", quote! { DefiniteLength::Vh(75.0) }),
|
||||
// ("screen", quote! { DefiniteLength::Vh(100.0) }),
|
||||
]
|
||||
}
|
||||
|
||||
fn tailwind_prefixes() -> Vec<(&'static str, bool, Vec<TokenStream2>)> {
|
||||
vec![
|
||||
("w", true, vec![quote! { size.width }]),
|
||||
("h", true, vec![quote! { size.height }]),
|
||||
("min_w", false, vec![quote! { min_size.width }]),
|
||||
("min_h", false, vec![quote! { min_size.height }]),
|
||||
("max_w", false, vec![quote! { max_size.width }]),
|
||||
("max_h", false, vec![quote! { max_size.height }]),
|
||||
(
|
||||
"m",
|
||||
true,
|
||||
vec![quote! { margin.top }, quote! { margin.bottom }],
|
||||
),
|
||||
("mt", true, vec![quote! { margin.top }]),
|
||||
("mb", true, vec![quote! { margin.bottom }]),
|
||||
(
|
||||
"mx",
|
||||
true,
|
||||
vec![quote! { margin.left }, quote! { margin.right }],
|
||||
),
|
||||
("ml", true, vec![quote! { margin.left }]),
|
||||
("mr", true, vec![quote! { margin.right }]),
|
||||
(
|
||||
"p",
|
||||
false,
|
||||
vec![quote! { padding.top }, quote! { padding.bottom }],
|
||||
),
|
||||
("pt", false, vec![quote! { padding.top }]),
|
||||
("pb", false, vec![quote! { padding.bottom }]),
|
||||
(
|
||||
"px",
|
||||
false,
|
||||
vec![quote! { padding.left }, quote! { padding.right }],
|
||||
),
|
||||
("pl", false, vec![quote! { padding.left }]),
|
||||
("pr", false, vec![quote! { padding.right }]),
|
||||
("top", true, vec![quote! { inset.top }]),
|
||||
("bottom", true, vec![quote! { inset.bottom }]),
|
||||
("left", true, vec![quote! { inset.left }]),
|
||||
("right", true, vec![quote! { inset.right }]),
|
||||
]
|
||||
}
|
99
crates/gpui/playground_macros/src/tailwind_lengths.rs
Normal file
99
crates/gpui/playground_macros/src/tailwind_lengths.rs
Normal file
@ -0,0 +1,99 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, FnArg, ItemFn, PatType};
|
||||
|
||||
pub fn tailwind_lengths(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input_function = parse_macro_input!(item as ItemFn);
|
||||
|
||||
let visibility = &input_function.vis;
|
||||
let function_signature = input_function.sig.clone();
|
||||
let function_body = input_function.block;
|
||||
let where_clause = &function_signature.generics.where_clause;
|
||||
|
||||
let argument_name = match function_signature.inputs.iter().nth(1) {
|
||||
Some(FnArg::Typed(PatType { pat, .. })) => pat,
|
||||
_ => panic!("Couldn't find the second argument in the function signature"),
|
||||
};
|
||||
|
||||
let mut output_functions = TokenStream2::new();
|
||||
|
||||
for (length, value) in fixed_lengths() {
|
||||
let function_name = format_ident!("{}{}", function_signature.ident, length);
|
||||
output_functions.extend(quote! {
|
||||
#visibility fn #function_name(mut self) -> Self #where_clause {
|
||||
let #argument_name = #value.into();
|
||||
#function_body
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
output_functions.into()
|
||||
}
|
||||
|
||||
fn fixed_lengths() -> Vec<(&'static str, TokenStream2)> {
|
||||
vec![
|
||||
("0", quote! { DefinedLength::Pixels(0.) }),
|
||||
("px", quote! { DefinedLength::Pixels(1.) }),
|
||||
("0_5", quote! { DefinedLength::Rems(0.125) }),
|
||||
("1", quote! { DefinedLength::Rems(0.25) }),
|
||||
("1_5", quote! { DefinedLength::Rems(0.375) }),
|
||||
("2", quote! { DefinedLength::Rems(0.5) }),
|
||||
("2_5", quote! { DefinedLength::Rems(0.625) }),
|
||||
("3", quote! { DefinedLength::Rems(0.75) }),
|
||||
("3_5", quote! { DefinedLength::Rems(0.875) }),
|
||||
("4", quote! { DefinedLength::Rems(1.) }),
|
||||
("5", quote! { DefinedLength::Rems(1.25) }),
|
||||
("6", quote! { DefinedLength::Rems(1.5) }),
|
||||
("7", quote! { DefinedLength::Rems(1.75) }),
|
||||
("8", quote! { DefinedLength::Rems(2.) }),
|
||||
("9", quote! { DefinedLength::Rems(2.25) }),
|
||||
("10", quote! { DefinedLength::Rems(2.5) }),
|
||||
("11", quote! { DefinedLength::Rems(2.75) }),
|
||||
("12", quote! { DefinedLength::Rems(3.) }),
|
||||
("14", quote! { DefinedLength::Rems(3.5) }),
|
||||
("16", quote! { DefinedLength::Rems(4.) }),
|
||||
("20", quote! { DefinedLength::Rems(5.) }),
|
||||
("24", quote! { DefinedLength::Rems(6.) }),
|
||||
("28", quote! { DefinedLength::Rems(7.) }),
|
||||
("32", quote! { DefinedLength::Rems(8.) }),
|
||||
("36", quote! { DefinedLength::Rems(9.) }),
|
||||
("40", quote! { DefinedLength::Rems(10.) }),
|
||||
("44", quote! { DefinedLength::Rems(11.) }),
|
||||
("48", quote! { DefinedLength::Rems(12.) }),
|
||||
("52", quote! { DefinedLength::Rems(13.) }),
|
||||
("56", quote! { DefinedLength::Rems(14.) }),
|
||||
("60", quote! { DefinedLength::Rems(15.) }),
|
||||
("64", quote! { DefinedLength::Rems(16.) }),
|
||||
("72", quote! { DefinedLength::Rems(18.) }),
|
||||
("80", quote! { DefinedLength::Rems(20.) }),
|
||||
("96", quote! { DefinedLength::Rems(24.) }),
|
||||
("half", quote! { DefinedLength::Percent(50.) }),
|
||||
("1_3rd", quote! { DefinedLength::Percent(33.333333) }),
|
||||
("2_3rd", quote! { DefinedLength::Percent(66.666667) }),
|
||||
("1_4th", quote! { DefinedLength::Percent(25.) }),
|
||||
("2_4th", quote! { DefinedLength::Percent(50.) }),
|
||||
("3_4th", quote! { DefinedLength::Percent(75.) }),
|
||||
("1_5th", quote! { DefinedLength::Percent(20.) }),
|
||||
("2_5th", quote! { DefinedLength::Percent(40.) }),
|
||||
("3_5th", quote! { DefinedLength::Percent(60.) }),
|
||||
("4_5th", quote! { DefinedLength::Percent(80.) }),
|
||||
("1_6th", quote! { DefinedLength::Percent(16.666667) }),
|
||||
("2_6th", quote! { DefinedLength::Percent(33.333333) }),
|
||||
("3_6th", quote! { DefinedLength::Percent(50.) }),
|
||||
("4_6th", quote! { DefinedLength::Percent(66.666667) }),
|
||||
("5_6th", quote! { DefinedLength::Percent(83.333333) }),
|
||||
("1_12th", quote! { DefinedLength::Percent(8.333333) }),
|
||||
("2_12th", quote! { DefinedLength::Percent(16.666667) }),
|
||||
("3_12th", quote! { DefinedLength::Percent(25.) }),
|
||||
("4_12th", quote! { DefinedLength::Percent(33.333333) }),
|
||||
("5_12th", quote! { DefinedLength::Percent(41.666667) }),
|
||||
("6_12th", quote! { DefinedLength::Percent(50.) }),
|
||||
("7_12th", quote! { DefinedLength::Percent(58.333333) }),
|
||||
("8_12th", quote! { DefinedLength::Percent(66.666667) }),
|
||||
("9_12th", quote! { DefinedLength::Percent(75.) }),
|
||||
("10_12th", quote! { DefinedLength::Percent(83.333333) }),
|
||||
("11_12th", quote! { DefinedLength::Percent(91.666667) }),
|
||||
("full", quote! { DefinedLength::Percent(100.) }),
|
||||
]
|
||||
}
|
@ -7,42 +7,6 @@ pub mod test_app_context;
|
||||
pub(crate) mod window;
|
||||
mod window_input_handler;
|
||||
|
||||
use std::{
|
||||
any::{type_name, Any, TypeId},
|
||||
cell::RefCell,
|
||||
fmt::{self, Debug},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
rc::{self, Rc},
|
||||
sync::{Arc, Weak},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use derive_more::Deref;
|
||||
use parking_lot::Mutex;
|
||||
use postage::oneshot;
|
||||
use smallvec::SmallVec;
|
||||
use smol::prelude::*;
|
||||
use util::ResultExt;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use action::*;
|
||||
use callback_collection::CallbackCollection;
|
||||
use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
|
||||
pub use menu::*;
|
||||
use platform::Event;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use ref_counts::LeakDetector;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use test_app_context::{ContextHandle, TestAppContext};
|
||||
use window_input_handler::WindowInputHandler;
|
||||
|
||||
use crate::{
|
||||
elements::{AnyElement, AnyRootElement, RootElement},
|
||||
executor::{self, Task},
|
||||
@ -57,8 +21,39 @@ use crate::{
|
||||
window::{Window, WindowContext},
|
||||
AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId,
|
||||
};
|
||||
|
||||
use self::ref_counts::RefCounts;
|
||||
pub use action::*;
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use callback_collection::CallbackCollection;
|
||||
use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
|
||||
use derive_more::Deref;
|
||||
pub use menu::*;
|
||||
use parking_lot::Mutex;
|
||||
use platform::Event;
|
||||
use postage::oneshot;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use ref_counts::LeakDetector;
|
||||
use ref_counts::RefCounts;
|
||||
use smallvec::SmallVec;
|
||||
use smol::prelude::*;
|
||||
use std::{
|
||||
any::{type_name, Any, TypeId},
|
||||
cell::RefCell,
|
||||
fmt::{self, Debug},
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
path::{Path, PathBuf},
|
||||
pin::Pin,
|
||||
rc::{self, Rc},
|
||||
sync::{Arc, Weak},
|
||||
time::Duration,
|
||||
};
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use test_app_context::{ContextHandle, TestAppContext};
|
||||
use util::ResultExt;
|
||||
use uuid::Uuid;
|
||||
use window_input_handler::WindowInputHandler;
|
||||
|
||||
pub trait Entity: 'static {
|
||||
type Event;
|
||||
@ -73,10 +68,12 @@ pub trait Entity: 'static {
|
||||
}
|
||||
|
||||
pub trait View: Entity + Sized {
|
||||
fn ui_name() -> &'static str;
|
||||
fn render(&mut self, cx: &mut ViewContext<'_, '_, Self>) -> AnyElement<Self>;
|
||||
fn focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
|
||||
fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
|
||||
fn ui_name() -> &'static str {
|
||||
type_name::<Self>()
|
||||
}
|
||||
fn key_down(&mut self, _: &KeyDownEvent, _: &mut ViewContext<Self>) -> bool {
|
||||
false
|
||||
}
|
||||
@ -640,7 +637,7 @@ impl AppContext {
|
||||
pub fn add_action<A, V, F, R>(&mut self, handler: F)
|
||||
where
|
||||
A: Action,
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
|
||||
{
|
||||
self.add_action_internal(handler, false)
|
||||
@ -649,7 +646,7 @@ impl AppContext {
|
||||
pub fn capture_action<A, V, F>(&mut self, handler: F)
|
||||
where
|
||||
A: Action,
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>),
|
||||
{
|
||||
self.add_action_internal(handler, true)
|
||||
@ -658,7 +655,7 @@ impl AppContext {
|
||||
fn add_action_internal<A, V, F, R>(&mut self, mut handler: F, capture: bool)
|
||||
where
|
||||
A: Action,
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> R,
|
||||
{
|
||||
let handler = Box::new(
|
||||
@ -699,7 +696,7 @@ impl AppContext {
|
||||
pub fn add_async_action<A, V, F>(&mut self, mut handler: F)
|
||||
where
|
||||
A: Action,
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: 'static + FnMut(&mut V, &A, &mut ViewContext<V>) -> Option<Task<Result<()>>>,
|
||||
{
|
||||
self.add_action(move |view, action, cx| {
|
||||
@ -898,8 +895,8 @@ impl AppContext {
|
||||
|
||||
fn observe_focus<F, V>(&mut self, handle: &ViewHandle<V>, mut callback: F) -> Subscription
|
||||
where
|
||||
V: 'static,
|
||||
F: 'static + FnMut(ViewHandle<V>, bool, &mut WindowContext) -> bool,
|
||||
V: View,
|
||||
{
|
||||
let subscription_id = post_inc(&mut self.next_subscription_id);
|
||||
let observed = handle.downgrade();
|
||||
@ -1382,15 +1379,15 @@ impl AppContext {
|
||||
self.windows.keys().copied()
|
||||
}
|
||||
|
||||
pub fn read_view<T: View>(&self, handle: &ViewHandle<T>) -> &T {
|
||||
pub fn read_view<V: 'static>(&self, handle: &ViewHandle<V>) -> &V {
|
||||
if let Some(view) = self.views.get(&(handle.window, handle.view_id)) {
|
||||
view.as_any().downcast_ref().expect("downcast is type safe")
|
||||
} else {
|
||||
panic!("circular view reference for type {}", type_name::<T>());
|
||||
panic!("circular view reference for type {}", type_name::<V>());
|
||||
}
|
||||
}
|
||||
|
||||
fn upgrade_view_handle<T: View>(&self, handle: &WeakViewHandle<T>) -> Option<ViewHandle<T>> {
|
||||
fn upgrade_view_handle<V: 'static>(&self, handle: &WeakViewHandle<V>) -> Option<ViewHandle<V>> {
|
||||
if self.ref_counts.lock().is_entity_alive(handle.view_id) {
|
||||
Some(ViewHandle::new(
|
||||
handle.window,
|
||||
@ -1659,6 +1656,9 @@ impl AppContext {
|
||||
subscription_id,
|
||||
callback,
|
||||
),
|
||||
Effect::RepaintWindow { window } => {
|
||||
self.handle_repaint_window_effect(window)
|
||||
}
|
||||
}
|
||||
self.pending_notifications.clear();
|
||||
} else {
|
||||
@ -1896,6 +1896,15 @@ impl AppContext {
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
|
||||
self.update_window(window, |cx| {
|
||||
cx.layout(false).log_err();
|
||||
if let Some(scene) = cx.paint().log_err() {
|
||||
cx.window.platform_window.present_scene(scene);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_window_activation_effect(&mut self, window: AnyWindowHandle, active: bool) -> bool {
|
||||
self.update_window(window, |cx| {
|
||||
if cx.window.is_active == active {
|
||||
@ -2151,7 +2160,7 @@ struct ViewMetadata {
|
||||
keymap_context: KeymapContext,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct WindowInvalidation {
|
||||
pub updated: HashSet<usize>,
|
||||
pub removed: Vec<usize>,
|
||||
@ -2255,6 +2264,9 @@ pub enum Effect {
|
||||
window: AnyWindowHandle,
|
||||
is_active: bool,
|
||||
},
|
||||
RepaintWindow {
|
||||
window: AnyWindowHandle,
|
||||
},
|
||||
WindowActivationObservation {
|
||||
window: AnyWindowHandle,
|
||||
subscription_id: usize,
|
||||
@ -2448,6 +2460,10 @@ impl Debug for Effect {
|
||||
.debug_struct("Effect::ActiveLabeledTasksObservation")
|
||||
.field("subscription_id", subscription_id)
|
||||
.finish(),
|
||||
Effect::RepaintWindow { window } => f
|
||||
.debug_struct("Effect::RepaintWindow")
|
||||
.field("window_id", &window.id())
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2543,10 +2559,7 @@ pub trait AnyView {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> AnyView for V
|
||||
where
|
||||
V: View,
|
||||
{
|
||||
impl<V: View> AnyView for V {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
@ -2878,7 +2891,7 @@ pub struct ViewContext<'a, 'b, T: ?Sized> {
|
||||
view_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: View> Deref for ViewContext<'a, 'b, T> {
|
||||
impl<'a, 'b, V> Deref for ViewContext<'a, 'b, V> {
|
||||
type Target = WindowContext<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -2886,14 +2899,14 @@ impl<'a, 'b, T: View> Deref for ViewContext<'a, 'b, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: View> DerefMut for ViewContext<'_, '_, T> {
|
||||
impl<'a, 'b, V> DerefMut for ViewContext<'a, 'b, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.window_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
||||
pub(crate) fn mutable(window_context: &'b mut WindowContext<'a>, view_id: usize) -> Self {
|
||||
impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
|
||||
pub fn mutable(window_context: &'b mut WindowContext<'a>, view_id: usize) -> Self {
|
||||
Self {
|
||||
window_context: Reference::Mutable(window_context),
|
||||
view_id,
|
||||
@ -2901,7 +2914,7 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn immutable(window_context: &'b WindowContext<'a>, view_id: usize) -> Self {
|
||||
pub fn immutable(window_context: &'b WindowContext<'a>, view_id: usize) -> Self {
|
||||
Self {
|
||||
window_context: Reference::Immutable(window_context),
|
||||
view_id,
|
||||
@ -2913,6 +2926,12 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
||||
&mut self.window_context
|
||||
}
|
||||
|
||||
pub fn notify(&mut self) {
|
||||
let window = self.window_handle;
|
||||
let view_id = self.view_id;
|
||||
self.window_context.notify_view(window, view_id);
|
||||
}
|
||||
|
||||
pub fn handle(&self) -> ViewHandle<V> {
|
||||
ViewHandle::new(
|
||||
self.window_handle,
|
||||
@ -3226,21 +3245,6 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn emit(&mut self, payload: V::Event) {
|
||||
self.window_context
|
||||
.pending_effects
|
||||
.push_back(Effect::Event {
|
||||
entity_id: self.view_id,
|
||||
payload: Box::new(payload),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn notify(&mut self) {
|
||||
let window = self.window_handle;
|
||||
let view_id = self.view_id;
|
||||
self.window_context.notify_view(window, view_id);
|
||||
}
|
||||
|
||||
pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut V, &mut ViewContext<V>)) {
|
||||
let handle = self.handle();
|
||||
self.window_context
|
||||
@ -3341,6 +3345,10 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
||||
self.element_state::<Tag, T>(element_id, T::default())
|
||||
}
|
||||
|
||||
pub fn rem_pixels(&self) -> f32 {
|
||||
16.
|
||||
}
|
||||
|
||||
pub fn default_element_state_dynamic<T: 'static + Default>(
|
||||
&mut self,
|
||||
tag: TypeTag,
|
||||
@ -3350,6 +3358,17 @@ impl<'a, 'b, V: View> ViewContext<'a, 'b, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> ViewContext<'_, '_, V> {
|
||||
pub fn emit(&mut self, event: V::Event) {
|
||||
self.window_context
|
||||
.pending_effects
|
||||
.push_back(Effect::Event {
|
||||
entity_id: self.view_id,
|
||||
payload: Box::new(event),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct TypeTag {
|
||||
tag: TypeId,
|
||||
@ -3428,15 +3447,27 @@ impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayoutContext<'a, 'b, 'c, V: View> {
|
||||
view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
/// Methods shared by both LayoutContext and PaintContext
|
||||
///
|
||||
/// It's that PaintContext should be implemented in terms of layout context and
|
||||
/// deref to it, in which case we wouldn't need this.
|
||||
pub trait RenderContext<'a, 'b, V> {
|
||||
fn text_style(&self) -> TextStyle;
|
||||
fn push_text_style(&mut self, style: TextStyle);
|
||||
fn pop_text_style(&mut self);
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V>;
|
||||
}
|
||||
|
||||
pub struct LayoutContext<'a, 'b, 'c, V> {
|
||||
// Nathan: Making this is public while I work on playground.
|
||||
pub view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
new_parents: &'c mut HashMap<usize, usize>,
|
||||
views_to_notify_if_ancestors_change: &'c mut HashMap<usize, SmallVec<[usize; 2]>>,
|
||||
text_style_stack: Vec<Arc<TextStyle>>,
|
||||
text_style_stack: Vec<TextStyle>,
|
||||
pub refreshing: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
|
||||
impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> {
|
||||
pub fn new(
|
||||
view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
new_parents: &'c mut HashMap<usize, usize>,
|
||||
@ -3500,26 +3531,39 @@ impl<'a, 'b, 'c, V: View> LayoutContext<'a, 'b, 'c, V> {
|
||||
.push(self_view_id);
|
||||
}
|
||||
|
||||
pub fn text_style(&self) -> Arc<TextStyle> {
|
||||
self.text_style_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or(Default::default())
|
||||
}
|
||||
|
||||
pub fn with_text_style<S, F, T>(&mut self, style: S, f: F) -> T
|
||||
pub fn with_text_style<F, T>(&mut self, style: TextStyle, f: F) -> T
|
||||
where
|
||||
S: Into<Arc<TextStyle>>,
|
||||
F: FnOnce(&mut Self) -> T,
|
||||
{
|
||||
self.text_style_stack.push(style.into());
|
||||
self.push_text_style(style);
|
||||
let result = f(self);
|
||||
self.text_style_stack.pop();
|
||||
self.pop_text_style();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V: View> Deref for LayoutContext<'a, 'b, 'c, V> {
|
||||
impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, 'c, V> {
|
||||
fn text_style(&self) -> TextStyle {
|
||||
self.text_style_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or(TextStyle::default(&self.font_cache))
|
||||
}
|
||||
|
||||
fn push_text_style(&mut self, style: TextStyle) {
|
||||
self.text_style_stack.push(style);
|
||||
}
|
||||
|
||||
fn pop_text_style(&mut self) {
|
||||
self.text_style_stack.pop();
|
||||
}
|
||||
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V> Deref for LayoutContext<'a, 'b, 'c, V> {
|
||||
type Target = ViewContext<'a, 'b, V>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -3527,13 +3571,13 @@ impl<'a, 'b, 'c, V: View> Deref for LayoutContext<'a, 'b, 'c, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> DerefMut for LayoutContext<'_, '_, '_, V> {
|
||||
impl<V> DerefMut for LayoutContext<'_, '_, '_, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
|
||||
impl<V> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
|
||||
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
|
||||
BorrowAppContext::read_with(&*self.view_context, f)
|
||||
}
|
||||
@ -3543,7 +3587,7 @@ impl<V: View> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
|
||||
impl<V> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
|
||||
@ -3573,39 +3617,42 @@ impl<V: View> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PaintContext<'a, 'b, 'c, V: View> {
|
||||
view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
text_style_stack: Vec<Arc<TextStyle>>,
|
||||
pub struct PaintContext<'a, 'b, 'c, V> {
|
||||
pub view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
text_style_stack: Vec<TextStyle>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V: View> PaintContext<'a, 'b, 'c, V> {
|
||||
impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> {
|
||||
pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
|
||||
Self {
|
||||
view_context,
|
||||
text_style_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_style(&self) -> Arc<TextStyle> {
|
||||
impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for PaintContext<'a, 'b, 'c, V> {
|
||||
fn text_style(&self) -> TextStyle {
|
||||
self.text_style_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or(Default::default())
|
||||
.unwrap_or(TextStyle::default(&self.font_cache))
|
||||
}
|
||||
|
||||
pub fn with_text_style<S, F, T>(&mut self, style: S, f: F) -> T
|
||||
where
|
||||
S: Into<Arc<TextStyle>>,
|
||||
F: FnOnce(&mut Self) -> T,
|
||||
{
|
||||
self.text_style_stack.push(style.into());
|
||||
let result = f(self);
|
||||
fn push_text_style(&mut self, style: TextStyle) {
|
||||
self.text_style_stack.push(style);
|
||||
}
|
||||
|
||||
fn pop_text_style(&mut self) {
|
||||
self.text_style_stack.pop();
|
||||
result
|
||||
}
|
||||
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V: View> Deref for PaintContext<'a, 'b, 'c, V> {
|
||||
impl<'a, 'b, 'c, V> Deref for PaintContext<'a, 'b, 'c, V> {
|
||||
type Target = ViewContext<'a, 'b, V>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -3613,13 +3660,13 @@ impl<'a, 'b, 'c, V: View> Deref for PaintContext<'a, 'b, 'c, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> DerefMut for PaintContext<'_, '_, '_, V> {
|
||||
impl<V> DerefMut for PaintContext<'_, '_, '_, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> BorrowAppContext for PaintContext<'_, '_, '_, V> {
|
||||
impl<V> BorrowAppContext for PaintContext<'_, '_, '_, V> {
|
||||
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
|
||||
BorrowAppContext::read_with(&*self.view_context, f)
|
||||
}
|
||||
@ -3629,7 +3676,7 @@ impl<V: View> BorrowAppContext for PaintContext<'_, '_, '_, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
|
||||
impl<V> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
|
||||
@ -3661,25 +3708,37 @@ impl<V: View> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventContext<'a, 'b, 'c, V: View> {
|
||||
pub struct EventContext<'a, 'b, 'c, V> {
|
||||
view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
pub(crate) handled: bool,
|
||||
// I would like to replace handled with this.
|
||||
// Being additive for now.
|
||||
pub bubble: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V: View> EventContext<'a, 'b, 'c, V> {
|
||||
pub(crate) fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
|
||||
impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
|
||||
pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
|
||||
EventContext {
|
||||
view_context,
|
||||
handled: true,
|
||||
bubble: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn propagate_event(&mut self) {
|
||||
self.handled = false;
|
||||
}
|
||||
|
||||
pub fn bubble_event(&mut self) {
|
||||
self.bubble = true;
|
||||
}
|
||||
|
||||
pub fn event_bubbled(&self) -> bool {
|
||||
self.bubble
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V: View> Deref for EventContext<'a, 'b, 'c, V> {
|
||||
impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> {
|
||||
type Target = ViewContext<'a, 'b, V>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -3687,13 +3746,13 @@ impl<'a, 'b, 'c, V: View> Deref for EventContext<'a, 'b, 'c, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> DerefMut for EventContext<'_, '_, '_, V> {
|
||||
impl<V> DerefMut for EventContext<'_, '_, '_, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
|
||||
impl<V> BorrowAppContext for EventContext<'_, '_, '_, V> {
|
||||
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
|
||||
BorrowAppContext::read_with(&*self.view_context, f)
|
||||
}
|
||||
@ -3703,7 +3762,7 @@ impl<V: View> BorrowAppContext for EventContext<'_, '_, '_, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> BorrowWindowContext for EventContext<'_, '_, '_, V> {
|
||||
impl<V> BorrowWindowContext for EventContext<'_, '_, '_, V> {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
|
||||
@ -4031,7 +4090,7 @@ impl<V> Clone for WindowHandle<V> {
|
||||
|
||||
impl<V> Copy for WindowHandle<V> {}
|
||||
|
||||
impl<V: View> WindowHandle<V> {
|
||||
impl<V: 'static> WindowHandle<V> {
|
||||
fn new(window_id: usize) -> Self {
|
||||
WindowHandle {
|
||||
any_handle: AnyWindowHandle::new(window_id, TypeId::of::<V>()),
|
||||
@ -4069,7 +4128,9 @@ impl<V: View> WindowHandle<V> {
|
||||
.update(cx, update)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> WindowHandle<V> {
|
||||
pub fn replace_root<C, F>(&self, cx: &mut C, build_root: F) -> C::Result<ViewHandle<V>>
|
||||
where
|
||||
C: BorrowWindowContext,
|
||||
@ -4149,7 +4210,7 @@ impl AnyWindowHandle {
|
||||
self.update(cx, |cx| cx.add_view(build_view))
|
||||
}
|
||||
|
||||
pub fn downcast<V: View>(self) -> Option<WindowHandle<V>> {
|
||||
pub fn downcast<V: 'static>(self) -> Option<WindowHandle<V>> {
|
||||
if self.root_view_type == TypeId::of::<V>() {
|
||||
Some(WindowHandle {
|
||||
any_handle: self,
|
||||
@ -4160,7 +4221,7 @@ impl AnyWindowHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_is<V: View>(&self) -> bool {
|
||||
pub fn root_is<V: 'static>(&self) -> bool {
|
||||
self.root_view_type == TypeId::of::<V>()
|
||||
}
|
||||
|
||||
@ -4238,9 +4299,9 @@ impl AnyWindowHandle {
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct ViewHandle<T> {
|
||||
pub struct ViewHandle<V> {
|
||||
any_handle: AnyViewHandle,
|
||||
view_type: PhantomData<T>,
|
||||
view_type: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<T> Deref for ViewHandle<T> {
|
||||
@ -4251,15 +4312,15 @@ impl<T> Deref for ViewHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: View> ViewHandle<T> {
|
||||
impl<V: 'static> ViewHandle<V> {
|
||||
fn new(window: AnyWindowHandle, view_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
|
||||
Self {
|
||||
any_handle: AnyViewHandle::new(window, view_id, TypeId::of::<T>(), ref_counts.clone()),
|
||||
any_handle: AnyViewHandle::new(window, view_id, TypeId::of::<V>(), ref_counts.clone()),
|
||||
view_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downgrade(&self) -> WeakViewHandle<T> {
|
||||
pub fn downgrade(&self) -> WeakViewHandle<V> {
|
||||
WeakViewHandle::new(self.window, self.view_id)
|
||||
}
|
||||
|
||||
@ -4275,14 +4336,14 @@ impl<T: View> ViewHandle<T> {
|
||||
self.view_id
|
||||
}
|
||||
|
||||
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
|
||||
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
|
||||
cx.read_view(self)
|
||||
}
|
||||
|
||||
pub fn read_with<C, F, S>(&self, cx: &C, read: F) -> C::Result<S>
|
||||
where
|
||||
C: BorrowWindowContext,
|
||||
F: FnOnce(&T, &ViewContext<T>) -> S,
|
||||
F: FnOnce(&V, &ViewContext<V>) -> S,
|
||||
{
|
||||
cx.read_window(self.window, |cx| {
|
||||
let cx = ViewContext::immutable(cx, self.view_id);
|
||||
@ -4293,7 +4354,7 @@ impl<T: View> ViewHandle<T> {
|
||||
pub fn update<C, F, S>(&self, cx: &mut C, update: F) -> C::Result<S>
|
||||
where
|
||||
C: BorrowWindowContext,
|
||||
F: FnOnce(&mut T, &mut ViewContext<T>) -> S,
|
||||
F: FnOnce(&mut V, &mut ViewContext<V>) -> S,
|
||||
{
|
||||
let mut update = Some(update);
|
||||
|
||||
@ -4429,8 +4490,8 @@ impl AnyViewHandle {
|
||||
TypeId::of::<T>() == self.view_type
|
||||
}
|
||||
|
||||
pub fn downcast<T: View>(self) -> Option<ViewHandle<T>> {
|
||||
if self.is::<T>() {
|
||||
pub fn downcast<V: 'static>(self) -> Option<ViewHandle<V>> {
|
||||
if self.is::<V>() {
|
||||
Some(ViewHandle {
|
||||
any_handle: self,
|
||||
view_type: PhantomData,
|
||||
@ -4440,8 +4501,8 @@ impl AnyViewHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn downcast_ref<T: View>(&self) -> Option<&ViewHandle<T>> {
|
||||
if self.is::<T>() {
|
||||
pub fn downcast_ref<V: 'static>(&self) -> Option<&ViewHandle<V>> {
|
||||
if self.is::<V>() {
|
||||
Some(unsafe { mem::transmute(self) })
|
||||
} else {
|
||||
None
|
||||
@ -4640,7 +4701,7 @@ impl<T> WeakHandle for WeakViewHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> WeakViewHandle<V> {
|
||||
impl<V: 'static> WeakViewHandle<V> {
|
||||
fn new(window: AnyWindowHandle, view_id: usize) -> Self {
|
||||
Self {
|
||||
any_handle: AnyWeakViewHandle {
|
||||
@ -4680,28 +4741,47 @@ impl<V: View> WeakViewHandle<V> {
|
||||
cx.read(|cx| {
|
||||
let handle = cx
|
||||
.upgrade_view_handle(self)
|
||||
.ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?;
|
||||
.ok_or_else(|| anyhow!("view was dropped"))?;
|
||||
cx.read_window(self.window, |cx| handle.read_with(cx, read))
|
||||
.ok_or_else(|| anyhow!("window was removed"))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update<T>(
|
||||
pub fn update<T, B>(
|
||||
&self,
|
||||
cx: &mut AsyncAppContext,
|
||||
cx: &mut B,
|
||||
update: impl FnOnce(&mut V, &mut ViewContext<V>) -> T,
|
||||
) -> Result<T> {
|
||||
cx.update(|cx| {
|
||||
let handle = cx
|
||||
.upgrade_view_handle(self)
|
||||
.ok_or_else(|| anyhow!("view {} was dropped", V::ui_name()))?;
|
||||
cx.update_window(self.window, |cx| handle.update(cx, update))
|
||||
.ok_or_else(|| anyhow!("window was removed"))
|
||||
) -> Result<T>
|
||||
where
|
||||
B: BorrowWindowContext,
|
||||
B::Result<Option<T>>: Flatten<T>,
|
||||
{
|
||||
cx.update_window(self.window(), |cx| {
|
||||
cx.upgrade_view_handle(self)
|
||||
.map(|handle| handle.update(cx, update))
|
||||
})
|
||||
.flatten()
|
||||
.ok_or_else(|| anyhow!("window was removed"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for WeakViewHandle<T> {
|
||||
pub trait Flatten<T> {
|
||||
fn flatten(self) -> Option<T>;
|
||||
}
|
||||
|
||||
impl<T> Flatten<T> for Option<Option<T>> {
|
||||
fn flatten(self) -> Option<T> {
|
||||
self.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Flatten<T> for Option<T> {
|
||||
fn flatten(self) -> Option<T> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Deref for WeakViewHandle<V> {
|
||||
type Target = AnyWeakViewHandle;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -4709,7 +4789,7 @@ impl<T> Deref for WeakViewHandle<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for WeakViewHandle<T> {
|
||||
impl<V> Clone for WeakViewHandle<V> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
any_handle: self.any_handle.clone(),
|
||||
@ -5263,6 +5343,7 @@ mod tests {
|
||||
button: MouseButton::Left,
|
||||
modifiers: Default::default(),
|
||||
click_count: 1,
|
||||
is_down: true,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
elements::AnyRootElement,
|
||||
geometry::rect::RectF,
|
||||
geometry::{rect::RectF, Size},
|
||||
json::ToJson,
|
||||
keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
|
||||
platform::{
|
||||
@ -8,8 +8,9 @@ use crate::{
|
||||
MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
|
||||
},
|
||||
scene::{
|
||||
CursorRegion, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag, MouseEvent,
|
||||
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene,
|
||||
CursorRegion, EventHandler, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
|
||||
MouseEvent, MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||
Scene,
|
||||
},
|
||||
text_layout::TextLayoutCache,
|
||||
util::post_inc,
|
||||
@ -31,7 +32,11 @@ use sqlez::{
|
||||
use std::{
|
||||
any::TypeId,
|
||||
mem,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
ops::{Deref, DerefMut, Range, Sub},
|
||||
};
|
||||
use taffy::{
|
||||
tree::{Measurable, MeasureFunc},
|
||||
Taffy,
|
||||
};
|
||||
use util::ResultExt;
|
||||
use uuid::Uuid;
|
||||
@ -39,6 +44,7 @@ use uuid::Uuid;
|
||||
use super::{Reference, ViewMetadata};
|
||||
|
||||
pub struct Window {
|
||||
layout_engines: Vec<LayoutEngine>,
|
||||
pub(crate) root_view: Option<AnyViewHandle>,
|
||||
pub(crate) focused_view_id: Option<usize>,
|
||||
pub(crate) parents: HashMap<usize, usize>,
|
||||
@ -51,6 +57,7 @@ pub struct Window {
|
||||
appearance: Appearance,
|
||||
cursor_regions: Vec<CursorRegion>,
|
||||
mouse_regions: Vec<(MouseRegion, usize)>,
|
||||
event_handlers: Vec<EventHandler>,
|
||||
last_mouse_moved_event: Option<Event>,
|
||||
pub(crate) hovered_region_ids: Vec<MouseRegionId>,
|
||||
pub(crate) clicked_region_ids: Vec<MouseRegionId>,
|
||||
@ -67,12 +74,13 @@ impl Window {
|
||||
build_view: F,
|
||||
) -> Self
|
||||
where
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
V: View,
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
{
|
||||
let titlebar_height = platform_window.titlebar_height();
|
||||
let appearance = platform_window.appearance();
|
||||
let mut window = Self {
|
||||
layout_engines: Vec::new(),
|
||||
root_view: None,
|
||||
focused_view_id: None,
|
||||
parents: Default::default(),
|
||||
@ -83,6 +91,7 @@ impl Window {
|
||||
rendered_views: Default::default(),
|
||||
cursor_regions: Default::default(),
|
||||
mouse_regions: Default::default(),
|
||||
event_handlers: Default::default(),
|
||||
text_layout_cache: TextLayoutCache::new(cx.font_system.clone()),
|
||||
last_mouse_moved_event: None,
|
||||
hovered_region_ids: Default::default(),
|
||||
@ -109,6 +118,10 @@ impl Window {
|
||||
.as_ref()
|
||||
.expect("root_view called during window construction")
|
||||
}
|
||||
|
||||
pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
|
||||
mem::take(&mut self.event_handlers)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WindowContext<'a> {
|
||||
@ -207,6 +220,24 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repaint(&mut self) {
|
||||
let window = self.window();
|
||||
self.pending_effects
|
||||
.push_back(Effect::RepaintWindow { window });
|
||||
}
|
||||
|
||||
pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
|
||||
self.window.layout_engines.last_mut()
|
||||
}
|
||||
|
||||
pub fn push_layout_engine(&mut self, engine: LayoutEngine) {
|
||||
self.window.layout_engines.push(engine);
|
||||
}
|
||||
|
||||
pub fn pop_layout_engine(&mut self) -> Option<LayoutEngine> {
|
||||
self.window.layout_engines.pop()
|
||||
}
|
||||
|
||||
pub fn remove_window(&mut self) {
|
||||
self.removed = true;
|
||||
}
|
||||
@ -227,6 +258,10 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.platform_window.content_size()
|
||||
}
|
||||
|
||||
pub fn mouse_position(&self) -> Vector2F {
|
||||
self.window.mouse_position
|
||||
}
|
||||
|
||||
pub fn text_layout_cache(&self) -> &TextLayoutCache {
|
||||
&self.window.text_layout_cache
|
||||
}
|
||||
@ -242,14 +277,11 @@ impl<'a> WindowContext<'a> {
|
||||
Some(result)
|
||||
}
|
||||
|
||||
pub(crate) fn update_view<T, S>(
|
||||
pub(crate) fn update_view<V: 'static, S>(
|
||||
&mut self,
|
||||
handle: &ViewHandle<T>,
|
||||
update: &mut dyn FnMut(&mut T, &mut ViewContext<T>) -> S,
|
||||
) -> S
|
||||
where
|
||||
T: View,
|
||||
{
|
||||
handle: &ViewHandle<V>,
|
||||
update: &mut dyn FnMut(&mut V, &mut ViewContext<V>) -> S,
|
||||
) -> S {
|
||||
self.update_any_view(handle.view_id, |view, cx| {
|
||||
let mut cx = ViewContext::mutable(cx, handle.view_id);
|
||||
update(
|
||||
@ -475,6 +507,8 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
|
||||
self.dispatch_to_new_event_handlers(&event);
|
||||
|
||||
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
||||
let mut notified_views: HashSet<usize> = Default::default();
|
||||
let handle = self.window_handle;
|
||||
@ -852,6 +886,18 @@ impl<'a> WindowContext<'a> {
|
||||
any_event_handled
|
||||
}
|
||||
|
||||
fn dispatch_to_new_event_handlers(&mut self, event: &Event) {
|
||||
if let Some(mouse_event) = event.mouse_event() {
|
||||
let event_handlers = self.window.take_event_handlers();
|
||||
for event_handler in event_handlers.iter().rev() {
|
||||
if event_handler.event_type == mouse_event.type_id() {
|
||||
(event_handler.handler)(mouse_event, self);
|
||||
}
|
||||
}
|
||||
self.window.event_handlers = event_handlers;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
|
||||
let handle = self.window_handle;
|
||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||
@ -942,14 +988,16 @@ impl<'a> WindowContext<'a> {
|
||||
Ok(element)
|
||||
}
|
||||
|
||||
pub(crate) fn layout(&mut self, refreshing: bool) -> Result<HashMap<usize, usize>> {
|
||||
pub fn layout(&mut self, refreshing: bool) -> Result<HashMap<usize, usize>> {
|
||||
let window_size = self.window.platform_window.content_size();
|
||||
let root_view_id = self.window.root_view().id();
|
||||
|
||||
let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
|
||||
|
||||
let mut new_parents = HashMap::default();
|
||||
let mut views_to_notify_if_ancestors_change = HashMap::default();
|
||||
rendered_root.layout(
|
||||
SizeConstraint::strict(window_size),
|
||||
SizeConstraint::new(window_size, window_size),
|
||||
&mut new_parents,
|
||||
&mut views_to_notify_if_ancestors_change,
|
||||
refreshing,
|
||||
@ -982,7 +1030,7 @@ impl<'a> WindowContext<'a> {
|
||||
Ok(old_parents)
|
||||
}
|
||||
|
||||
pub(crate) fn paint(&mut self) -> Result<Scene> {
|
||||
pub fn paint(&mut self) -> Result<Scene> {
|
||||
let window_size = self.window.platform_window.content_size();
|
||||
let scale_factor = self.window.platform_window.scale_factor();
|
||||
|
||||
@ -1001,9 +1049,10 @@ impl<'a> WindowContext<'a> {
|
||||
.insert(root_view_id, rendered_root);
|
||||
|
||||
self.window.text_layout_cache.finish_frame();
|
||||
let scene = scene_builder.build();
|
||||
let mut scene = scene_builder.build();
|
||||
self.window.cursor_regions = scene.cursor_regions();
|
||||
self.window.mouse_regions = scene.mouse_regions();
|
||||
self.window.event_handlers = scene.take_event_handlers();
|
||||
|
||||
if self.window_is_active() {
|
||||
if let Some(event) = self.window.last_mouse_moved_event.clone() {
|
||||
@ -1014,6 +1063,11 @@ impl<'a> WindowContext<'a> {
|
||||
Ok(scene)
|
||||
}
|
||||
|
||||
pub fn root_element(&self) -> &Box<dyn AnyRootElement> {
|
||||
let view_id = self.window.root_view().id();
|
||||
self.window.rendered_views.get(&view_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn rect_for_text_range(&self, range_utf16: Range<usize>) -> Option<RectF> {
|
||||
let focused_view_id = self.window.focused_view_id?;
|
||||
self.window
|
||||
@ -1216,6 +1270,119 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LayoutEngine(Taffy);
|
||||
pub use taffy::style::Style as LayoutStyle;
|
||||
|
||||
impl LayoutEngine {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn add_node<C>(&mut self, style: LayoutStyle, children: C) -> Result<LayoutId>
|
||||
where
|
||||
C: IntoIterator<Item = LayoutId>,
|
||||
{
|
||||
Ok(self
|
||||
.0
|
||||
.new_with_children(style, &children.into_iter().collect::<Vec<_>>())?)
|
||||
}
|
||||
|
||||
pub fn add_measured_node<F>(&mut self, style: LayoutStyle, measure: F) -> Result<LayoutId>
|
||||
where
|
||||
F: Fn(MeasureParams) -> Size<f32> + Sync + Send + 'static,
|
||||
{
|
||||
Ok(self
|
||||
.0
|
||||
.new_leaf_with_measure(style, MeasureFunc::Boxed(Box::new(MeasureFn(measure))))?)
|
||||
}
|
||||
|
||||
pub fn compute_layout(&mut self, root: LayoutId, available_space: Vector2F) -> Result<()> {
|
||||
self.0.compute_layout(
|
||||
root,
|
||||
taffy::geometry::Size {
|
||||
width: available_space.x().into(),
|
||||
height: available_space.y().into(),
|
||||
},
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
|
||||
Ok(self.0.layout(node)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MeasureFn<F>(F);
|
||||
|
||||
impl<F: Send + Sync> Measurable for MeasureFn<F>
|
||||
where
|
||||
F: Fn(MeasureParams) -> Size<f32>,
|
||||
{
|
||||
fn measure(
|
||||
&self,
|
||||
known_dimensions: taffy::prelude::Size<Option<f32>>,
|
||||
available_space: taffy::prelude::Size<taffy::style::AvailableSpace>,
|
||||
) -> taffy::prelude::Size<f32> {
|
||||
(self.0)(MeasureParams {
|
||||
known_dimensions: known_dimensions.into(),
|
||||
available_space: available_space.into(),
|
||||
})
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EngineLayout {
|
||||
pub bounds: RectF,
|
||||
pub order: u32,
|
||||
}
|
||||
|
||||
pub struct MeasureParams {
|
||||
pub known_dimensions: Size<Option<f32>>,
|
||||
pub available_space: Size<AvailableSpace>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AvailableSpace {
|
||||
/// The amount of space available is the specified number of pixels
|
||||
Pixels(f32),
|
||||
/// The amount of space available is indefinite and the node should be laid out under a min-content constraint
|
||||
MinContent,
|
||||
/// The amount of space available is indefinite and the node should be laid out under a max-content constraint
|
||||
MaxContent,
|
||||
}
|
||||
|
||||
impl Default for AvailableSpace {
|
||||
fn default() -> Self {
|
||||
Self::Pixels(0.)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
|
||||
fn from(value: taffy::prelude::AvailableSpace) -> Self {
|
||||
match value {
|
||||
taffy::prelude::AvailableSpace::Definite(pixels) => Self::Pixels(pixels),
|
||||
taffy::prelude::AvailableSpace::MinContent => Self::MinContent,
|
||||
taffy::prelude::AvailableSpace::MaxContent => Self::MaxContent,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&taffy::tree::Layout> for EngineLayout {
|
||||
fn from(value: &taffy::tree::Layout) -> Self {
|
||||
Self {
|
||||
bounds: RectF::new(
|
||||
vec2f(value.location.x, value.location.y),
|
||||
vec2f(value.size.width, value.size.height),
|
||||
),
|
||||
order: value.order,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type LayoutId = taffy::prelude::NodeId;
|
||||
|
||||
pub struct RenderParams {
|
||||
pub view_id: usize,
|
||||
pub titlebar_height: f32,
|
||||
@ -1324,6 +1491,12 @@ impl SizeConstraint {
|
||||
max: size,
|
||||
}
|
||||
}
|
||||
pub fn loose(max: Vector2F) -> Self {
|
||||
Self {
|
||||
min: Vector2F::zero(),
|
||||
max,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strict_along(axis: Axis, max: f32) -> Self {
|
||||
match axis {
|
||||
@ -1360,6 +1533,17 @@ impl SizeConstraint {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Vector2F> for SizeConstraint {
|
||||
type Output = SizeConstraint;
|
||||
|
||||
fn sub(self, rhs: Vector2F) -> SizeConstraint {
|
||||
SizeConstraint {
|
||||
min: self.min - rhs,
|
||||
max: self.max - rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SizeConstraint {
|
||||
fn default() -> Self {
|
||||
SizeConstraint {
|
||||
@ -1378,6 +1562,7 @@ impl ToJson for SizeConstraint {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChildView {
|
||||
view_id: usize,
|
||||
view_name: &'static str,
|
||||
@ -1393,7 +1578,7 @@ impl ChildView {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for ChildView {
|
||||
impl<V: 'static> Element<V> for ChildView {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -15,35 +15,75 @@ use serde_json::json;
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, JsonSchema)]
|
||||
#[repr(transparent)]
|
||||
pub struct Color(#[schemars(with = "String")] ColorU);
|
||||
pub struct Color(#[schemars(with = "String")] pub ColorU);
|
||||
|
||||
pub fn color(rgba: u32) -> Color {
|
||||
Color::from_u32(rgba)
|
||||
}
|
||||
|
||||
pub fn rgb(r: f32, g: f32, b: f32) -> Color {
|
||||
Color(ColorF::new(r, g, b, 1.).to_u8())
|
||||
}
|
||||
|
||||
pub fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
|
||||
Color(ColorF::new(r, g, b, a).to_u8())
|
||||
}
|
||||
|
||||
pub fn transparent_black() -> Color {
|
||||
Color(ColorU::transparent_black())
|
||||
}
|
||||
|
||||
pub fn black() -> Color {
|
||||
Color(ColorU::black())
|
||||
}
|
||||
|
||||
pub fn white() -> Color {
|
||||
Color(ColorU::white())
|
||||
}
|
||||
|
||||
pub fn red() -> Color {
|
||||
color(0xff0000ff)
|
||||
}
|
||||
|
||||
pub fn green() -> Color {
|
||||
color(0x00ff00ff)
|
||||
}
|
||||
|
||||
pub fn blue() -> Color {
|
||||
color(0x0000ffff)
|
||||
}
|
||||
|
||||
pub fn yellow() -> Color {
|
||||
color(0xffff00ff)
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn transparent_black() -> Self {
|
||||
Self(ColorU::transparent_black())
|
||||
transparent_black()
|
||||
}
|
||||
|
||||
pub fn black() -> Self {
|
||||
Self(ColorU::black())
|
||||
black()
|
||||
}
|
||||
|
||||
pub fn white() -> Self {
|
||||
Self(ColorU::white())
|
||||
white()
|
||||
}
|
||||
|
||||
pub fn red() -> Self {
|
||||
Self(ColorU::from_u32(0xff0000ff))
|
||||
Color::from_u32(0xff0000ff)
|
||||
}
|
||||
|
||||
pub fn green() -> Self {
|
||||
Self(ColorU::from_u32(0x00ff00ff))
|
||||
Color::from_u32(0x00ff00ff)
|
||||
}
|
||||
|
||||
pub fn blue() -> Self {
|
||||
Self(ColorU::from_u32(0x0000ffff))
|
||||
Color::from_u32(0x0000ffff)
|
||||
}
|
||||
|
||||
pub fn yellow() -> Self {
|
||||
Self(ColorU::from_u32(0xffff00ff))
|
||||
Color::from_u32(0xffff00ff)
|
||||
}
|
||||
|
||||
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
@ -101,6 +141,12 @@ impl<'de> Deserialize<'de> for Color {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Color {
|
||||
fn from(value: u32) -> Self {
|
||||
Self(ColorU::from_u32(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Color {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
json!(format!(
|
||||
|
@ -34,7 +34,7 @@ use crate::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json, Action, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
|
||||
json, Action, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
|
||||
ViewContext, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
@ -42,14 +42,19 @@ use collections::HashMap;
|
||||
use core::panic;
|
||||
use json::ToJson;
|
||||
use smallvec::SmallVec;
|
||||
use std::{any::Any, borrow::Cow, mem, ops::Range};
|
||||
use std::{
|
||||
any::{type_name, Any},
|
||||
borrow::Cow,
|
||||
mem,
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
pub trait Element<V: View>: 'static {
|
||||
pub trait Element<V: 'static>: 'static {
|
||||
type LayoutState;
|
||||
type PaintState;
|
||||
|
||||
fn view_name(&self) -> &'static str {
|
||||
V::ui_name()
|
||||
type_name::<V>()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
@ -252,11 +257,7 @@ pub trait Element<V: View>: 'static {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RenderElement {
|
||||
fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||
}
|
||||
|
||||
trait AnyElementState<V: View> {
|
||||
trait AnyElementState<V> {
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
@ -270,7 +271,7 @@ trait AnyElementState<V: View> {
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
);
|
||||
|
||||
fn rect_for_text_range(
|
||||
@ -287,7 +288,7 @@ trait AnyElementState<V: View> {
|
||||
fn metadata(&self) -> Option<&dyn Any>;
|
||||
}
|
||||
|
||||
enum ElementState<V: View, E: Element<V>> {
|
||||
enum ElementState<V: 'static, E: Element<V>> {
|
||||
Empty,
|
||||
Init {
|
||||
element: E,
|
||||
@ -308,7 +309,7 @@ enum ElementState<V: View, E: Element<V>> {
|
||||
},
|
||||
}
|
||||
|
||||
impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
@ -351,7 +352,7 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
*self = match mem::take(self) {
|
||||
ElementState::PostLayout {
|
||||
@ -490,18 +491,18 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View, E: Element<V>> Default for ElementState<V, E> {
|
||||
impl<V, E: Element<V>> Default for ElementState<V, E> {
|
||||
fn default() -> Self {
|
||||
Self::Empty
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyElement<V: View> {
|
||||
pub struct AnyElement<V> {
|
||||
state: Box<dyn AnyElementState<V>>,
|
||||
name: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
impl<V: View> AnyElement<V> {
|
||||
impl<V> AnyElement<V> {
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.name.as_deref()
|
||||
}
|
||||
@ -527,7 +528,7 @@ impl<V: View> AnyElement<V> {
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
cx: &mut PaintContext<V>,
|
||||
) {
|
||||
self.state.paint(scene, origin, visible_bounds, view, cx);
|
||||
}
|
||||
@ -569,7 +570,7 @@ impl<V: View> AnyElement<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for AnyElement<V> {
|
||||
impl<V: 'static> Element<V> for AnyElement<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
@ -627,12 +628,18 @@ impl<V: View> Element<V> for AnyElement<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RootElement<V: View> {
|
||||
impl Entity for AnyElement<()> {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
// impl View for AnyElement<()> {}
|
||||
|
||||
pub struct RootElement<V> {
|
||||
element: AnyElement<V>,
|
||||
view: WeakViewHandle<V>,
|
||||
}
|
||||
|
||||
impl<V: View> RootElement<V> {
|
||||
impl<V> RootElement<V> {
|
||||
pub fn new(element: AnyElement<V>, view: WeakViewHandle<V>) -> Self {
|
||||
Self { element, view }
|
||||
}
|
||||
@ -700,7 +707,9 @@ impl<V: View> AnyRootElement for RootElement<V> {
|
||||
.ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
self.element.paint(scene, origin, visible_bounds, view, cx);
|
||||
let mut cx = PaintContext::new(cx);
|
||||
self.element
|
||||
.paint(scene, origin, visible_bounds, view, &mut cx);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
@ -740,7 +749,7 @@ impl<V: View> AnyRootElement for RootElement<V> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
|
||||
pub trait ParentElement<'a, V: 'static>: Extend<AnyElement<V>> + Sized {
|
||||
fn add_children<E: Element<V>>(&mut self, children: impl IntoIterator<Item = E>) {
|
||||
self.extend(children.into_iter().map(|child| child.into_any()));
|
||||
}
|
||||
@ -760,7 +769,12 @@ pub trait ParentElement<'a, V: View>: Extend<AnyElement<V>> + Sized {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, V: View, T> ParentElement<'a, V> for T where T: Extend<AnyElement<V>> {}
|
||||
impl<'a, V, T> ParentElement<'a, V> for T
|
||||
where
|
||||
V: 'static,
|
||||
T: Extend<AnyElement<V>>,
|
||||
{
|
||||
}
|
||||
|
||||
pub fn constrain_size_preserving_aspect_ratio(max_size: Vector2F, size: Vector2F) -> Vector2F {
|
||||
if max_size.x().is_infinite() && max_size.y().is_infinite() {
|
||||
|
@ -1,18 +1,18 @@
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
|
||||
use serde_json::json;
|
||||
|
||||
pub struct Align<V: View> {
|
||||
pub struct Align<V> {
|
||||
child: AnyElement<V>,
|
||||
alignment: Vector2F,
|
||||
}
|
||||
|
||||
impl<V: View> Align<V> {
|
||||
impl<V> Align<V> {
|
||||
pub fn new(child: AnyElement<V>) -> Self {
|
||||
Self {
|
||||
child,
|
||||
@ -41,7 +41,7 @@ impl<V: View> Align<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Align<V> {
|
||||
impl<V: 'static> Element<V> for Align<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
||||
use super::Element;
|
||||
use crate::{
|
||||
json::{self, json},
|
||||
PaintContext, SceneBuilder, View, ViewContext,
|
||||
PaintContext, SceneBuilder, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use pathfinder_geometry::{
|
||||
@ -15,7 +15,6 @@ pub struct Canvas<V, F>(F, PhantomData<V>);
|
||||
|
||||
impl<V, F> Canvas<V, F>
|
||||
where
|
||||
V: View,
|
||||
F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
|
||||
{
|
||||
pub fn new(f: F) -> Self {
|
||||
@ -23,7 +22,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View, F> Element<V> for Canvas<V, F>
|
||||
impl<V: 'static, F> Element<V> for Canvas<V, F>
|
||||
where
|
||||
F: 'static + FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
|
||||
{
|
||||
|
@ -4,21 +4,21 @@ use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct Clipped<V: View> {
|
||||
pub struct Clipped<V> {
|
||||
child: AnyElement<V>,
|
||||
}
|
||||
|
||||
impl<V: View> Clipped<V> {
|
||||
impl<V> Clipped<V> {
|
||||
pub fn new(child: AnyElement<V>) -> Self {
|
||||
Self { child }
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Clipped<V> {
|
||||
impl<V: 'static> Element<V> for Clipped<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -3,17 +3,16 @@ use std::{any::Any, marker::PhantomData};
|
||||
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
|
||||
use crate::{
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
};
|
||||
|
||||
use super::Empty;
|
||||
|
||||
/// The core stateless component trait, simply rendering an element tree
|
||||
pub trait Component {
|
||||
fn render<V: View>(self, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||
fn render<V: 'static>(self, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||
|
||||
fn element<V: View>(self) -> ComponentAdapter<V, Self>
|
||||
fn element<V: 'static>(self) -> ComponentAdapter<V, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@ -27,7 +26,7 @@ pub trait Component {
|
||||
StylableAdapter::new(self)
|
||||
}
|
||||
|
||||
fn stateful<V: View>(self) -> StatefulAdapter<Self, V>
|
||||
fn stateful<V: 'static>(self) -> StatefulAdapter<Self, V>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
@ -91,7 +90,7 @@ impl<C: Component> SafeStylable for StylableAdapter<C> {
|
||||
/// want to take click handler callbacks Unfortunately, the generic bound on the
|
||||
/// Component trait makes it incompatible with the stateless components above.
|
||||
// So let's just replicate them for now
|
||||
pub trait StatefulComponent<V: View> {
|
||||
pub trait StatefulComponent<V: 'static> {
|
||||
fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||
|
||||
fn element(self) -> ComponentAdapter<V, Self>
|
||||
@ -118,21 +117,21 @@ pub trait StatefulComponent<V: View> {
|
||||
|
||||
/// It is trivial to convert stateless components to stateful components, so lets
|
||||
/// do so en masse. Note that the reverse is impossible without a helper.
|
||||
impl<V: View, C: Component> StatefulComponent<V> for C {
|
||||
impl<V: 'static, C: Component> StatefulComponent<V> for C {
|
||||
fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
self.render(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as stylable, but generic over a view type
|
||||
pub trait StatefulStylable<V: View>: StatefulComponent<V> {
|
||||
pub trait StatefulStylable<V: 'static>: StatefulComponent<V> {
|
||||
type Style: Clone;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self;
|
||||
}
|
||||
|
||||
/// Same as SafeStylable, but generic over a view type
|
||||
pub trait StatefulSafeStylable<V: View> {
|
||||
pub trait StatefulSafeStylable<V: 'static> {
|
||||
type Style: Clone;
|
||||
type Output: StatefulComponent<V>;
|
||||
|
||||
@ -140,7 +139,7 @@ pub trait StatefulSafeStylable<V: View> {
|
||||
}
|
||||
|
||||
/// Converting from stateless to stateful
|
||||
impl<V: View, C: SafeStylable> StatefulSafeStylable<V> for C {
|
||||
impl<V: 'static, C: SafeStylable> StatefulSafeStylable<V> for C {
|
||||
type Style = C::Style;
|
||||
|
||||
type Output = C::Output;
|
||||
@ -156,7 +155,7 @@ pub struct StatefulAdapter<C, V> {
|
||||
phantom: std::marker::PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<C: Component, V: View> StatefulAdapter<C, V> {
|
||||
impl<C: Component, V: 'static> StatefulAdapter<C, V> {
|
||||
pub fn new(component: C) -> Self {
|
||||
Self {
|
||||
component,
|
||||
@ -165,7 +164,7 @@ impl<C: Component, V: View> StatefulAdapter<C, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Component, V: View> StatefulComponent<V> for StatefulAdapter<C, V> {
|
||||
impl<C: Component, V: 'static> StatefulComponent<V> for StatefulAdapter<C, V> {
|
||||
fn render(self, _: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
self.component.render(cx)
|
||||
}
|
||||
@ -173,12 +172,12 @@ impl<C: Component, V: View> StatefulComponent<V> for StatefulAdapter<C, V> {
|
||||
|
||||
// A helper for converting stateful but style-less components into stylable ones
|
||||
// by using `()` as the style type
|
||||
pub struct StatefulStylableAdapter<C: StatefulComponent<V>, V: View> {
|
||||
pub struct StatefulStylableAdapter<C: StatefulComponent<V>, V: 'static> {
|
||||
component: C,
|
||||
phantom: std::marker::PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<C: StatefulComponent<V>, V: View> StatefulStylableAdapter<C, V> {
|
||||
impl<C: StatefulComponent<V>, V: 'static> StatefulStylableAdapter<C, V> {
|
||||
pub fn new(component: C) -> Self {
|
||||
Self {
|
||||
component,
|
||||
@ -187,7 +186,9 @@ impl<C: StatefulComponent<V>, V: View> StatefulStylableAdapter<C, V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: StatefulComponent<V>, V: View> StatefulSafeStylable<V> for StatefulStylableAdapter<C, V> {
|
||||
impl<C: StatefulComponent<V>, V: 'static> StatefulSafeStylable<V>
|
||||
for StatefulStylableAdapter<C, V>
|
||||
{
|
||||
type Style = ();
|
||||
|
||||
type Output = C;
|
||||
@ -205,7 +206,7 @@ pub struct StatelessElementAdapter {
|
||||
}
|
||||
|
||||
impl StatelessElementAdapter {
|
||||
pub fn new<V: View>(element: AnyElement<V>) -> Self {
|
||||
pub fn new<V: 'static>(element: AnyElement<V>) -> Self {
|
||||
StatelessElementAdapter {
|
||||
element: Box::new(element) as Box<dyn Any>,
|
||||
}
|
||||
@ -213,7 +214,7 @@ impl StatelessElementAdapter {
|
||||
}
|
||||
|
||||
impl Component for StatelessElementAdapter {
|
||||
fn render<V: View>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
*self
|
||||
.element
|
||||
.downcast::<AnyElement<V>>()
|
||||
@ -222,12 +223,12 @@ impl Component for StatelessElementAdapter {
|
||||
}
|
||||
|
||||
// For converting elements into stateful components
|
||||
pub struct StatefulElementAdapter<V: View> {
|
||||
pub struct StatefulElementAdapter<V: 'static> {
|
||||
element: AnyElement<V>,
|
||||
_phantom: std::marker::PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V: View> StatefulElementAdapter<V> {
|
||||
impl<V: 'static> StatefulElementAdapter<V> {
|
||||
pub fn new(element: AnyElement<V>) -> Self {
|
||||
Self {
|
||||
element,
|
||||
@ -236,7 +237,7 @@ impl<V: View> StatefulElementAdapter<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> StatefulComponent<V> for StatefulElementAdapter<V> {
|
||||
impl<V: 'static> StatefulComponent<V> for StatefulElementAdapter<V> {
|
||||
fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
self.element
|
||||
}
|
||||
@ -244,7 +245,7 @@ impl<V: View> StatefulComponent<V> for StatefulElementAdapter<V> {
|
||||
|
||||
/// A convenient shorthand for creating an empty component.
|
||||
impl Component for () {
|
||||
fn render<V: View>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
fn render<V: 'static>(self, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
}
|
||||
@ -258,13 +259,13 @@ impl Stylable for () {
|
||||
}
|
||||
|
||||
// For converting components back into Elements
|
||||
pub struct ComponentAdapter<V: View, E> {
|
||||
pub struct ComponentAdapter<V: 'static, E> {
|
||||
component: Option<E>,
|
||||
element: Option<AnyElement<V>>,
|
||||
phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<E, V: View> ComponentAdapter<V, E> {
|
||||
impl<E, V: 'static> ComponentAdapter<V, E> {
|
||||
pub fn new(e: E) -> Self {
|
||||
Self {
|
||||
component: Some(e),
|
||||
@ -274,7 +275,7 @@ impl<E, V: View> ComponentAdapter<V, E> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdapter<V, C> {
|
||||
impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdapter<V, C> {
|
||||
type LayoutState = ();
|
||||
|
||||
type PaintState = ();
|
||||
|
@ -5,21 +5,21 @@ use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
pub struct ConstrainedBox<V: View> {
|
||||
pub struct ConstrainedBox<V> {
|
||||
child: AnyElement<V>,
|
||||
constraint: Constraint<V>,
|
||||
}
|
||||
|
||||
pub enum Constraint<V: View> {
|
||||
pub enum Constraint<V> {
|
||||
Static(SizeConstraint),
|
||||
Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint>),
|
||||
}
|
||||
|
||||
impl<V: View> ToJson for Constraint<V> {
|
||||
impl<V> ToJson for Constraint<V> {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
match self {
|
||||
Constraint::Static(constraint) => constraint.to_json(),
|
||||
@ -28,7 +28,7 @@ impl<V: View> ToJson for Constraint<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> ConstrainedBox<V> {
|
||||
impl<V: 'static> ConstrainedBox<V> {
|
||||
pub fn new(child: impl Element<V>) -> Self {
|
||||
Self {
|
||||
child: child.into_any(),
|
||||
@ -132,7 +132,7 @@ impl<V: View> ConstrainedBox<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for ConstrainedBox<V> {
|
||||
impl<V: 'static> Element<V> for ConstrainedBox<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -10,8 +10,7 @@ use crate::{
|
||||
json::ToJson,
|
||||
platform::CursorStyle,
|
||||
scene::{self, Border, CornerRadii, CursorRegion, Quad},
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -55,12 +54,12 @@ impl ContainerStyle {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Container<V: View> {
|
||||
pub struct Container<V> {
|
||||
child: AnyElement<V>,
|
||||
style: ContainerStyle,
|
||||
}
|
||||
|
||||
impl<V: View> Container<V> {
|
||||
impl<V> Container<V> {
|
||||
pub fn new(child: AnyElement<V>) -> Self {
|
||||
Self {
|
||||
child,
|
||||
@ -207,7 +206,7 @@ impl<V: View> Container<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Container<V> {
|
||||
impl<V: 'static> Element<V> for Container<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
@ -358,8 +357,8 @@ impl ToJson for ContainerStyle {
|
||||
#[derive(Clone, Copy, Debug, Default, JsonSchema)]
|
||||
pub struct Margin {
|
||||
pub top: f32,
|
||||
pub left: f32,
|
||||
pub bottom: f32,
|
||||
pub left: f32,
|
||||
pub right: f32,
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
LayoutContext, PaintContext, SceneBuilder, View, ViewContext,
|
||||
LayoutContext, PaintContext, SceneBuilder, ViewContext,
|
||||
};
|
||||
use crate::{Element, SizeConstraint};
|
||||
|
||||
@ -26,7 +26,7 @@ impl Empty {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Empty {
|
||||
impl<V: 'static> Element<V> for Empty {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -2,18 +2,18 @@ use std::ops::Range;
|
||||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
pub struct Expanded<V: View> {
|
||||
pub struct Expanded<V> {
|
||||
child: AnyElement<V>,
|
||||
full_width: bool,
|
||||
full_height: bool,
|
||||
}
|
||||
|
||||
impl<V: View> Expanded<V> {
|
||||
impl<V: 'static> Expanded<V> {
|
||||
pub fn new(child: impl Element<V>) -> Self {
|
||||
Self {
|
||||
child: child.into_any(),
|
||||
@ -35,7 +35,7 @@ impl<V: View> Expanded<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Expanded<V> {
|
||||
impl<V: 'static> Element<V> for Expanded<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -3,7 +3,7 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
|
||||
use crate::{
|
||||
json::{self, ToJson, Value},
|
||||
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder,
|
||||
SizeConstraint, Vector2FExt, View, ViewContext,
|
||||
SizeConstraint, Vector2FExt, ViewContext,
|
||||
};
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
@ -17,7 +17,7 @@ struct ScrollState {
|
||||
scroll_position: Cell<f32>,
|
||||
}
|
||||
|
||||
pub struct Flex<V: View> {
|
||||
pub struct Flex<V> {
|
||||
axis: Axis,
|
||||
children: Vec<AnyElement<V>>,
|
||||
scroll_state: Option<(ElementStateHandle<Rc<ScrollState>>, usize)>,
|
||||
@ -25,7 +25,7 @@ pub struct Flex<V: View> {
|
||||
spacing: f32,
|
||||
}
|
||||
|
||||
impl<V: View> Flex<V> {
|
||||
impl<V: 'static> Flex<V> {
|
||||
pub fn new(axis: Axis) -> Self {
|
||||
Self {
|
||||
axis,
|
||||
@ -127,13 +127,13 @@ impl<V: View> Flex<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Extend<AnyElement<V>> for Flex<V> {
|
||||
impl<V> Extend<AnyElement<V>> for Flex<V> {
|
||||
fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
|
||||
self.children.extend(children);
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Flex<V> {
|
||||
impl<V: 'static> Element<V> for Flex<V> {
|
||||
type LayoutState = f32;
|
||||
type PaintState = ();
|
||||
|
||||
@ -422,12 +422,12 @@ struct FlexParentData {
|
||||
float: bool,
|
||||
}
|
||||
|
||||
pub struct FlexItem<V: View> {
|
||||
pub struct FlexItem<V> {
|
||||
metadata: FlexParentData,
|
||||
child: AnyElement<V>,
|
||||
}
|
||||
|
||||
impl<V: View> FlexItem<V> {
|
||||
impl<V: 'static> FlexItem<V> {
|
||||
pub fn new(child: impl Element<V>) -> Self {
|
||||
FlexItem {
|
||||
metadata: FlexParentData {
|
||||
@ -449,7 +449,7 @@ impl<V: View> FlexItem<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for FlexItem<V> {
|
||||
impl<V: 'static> Element<V> for FlexItem<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -3,16 +3,15 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
};
|
||||
|
||||
pub struct Hook<V: View> {
|
||||
pub struct Hook<V> {
|
||||
child: AnyElement<V>,
|
||||
after_layout: Option<Box<dyn FnMut(Vector2F, &mut ViewContext<V>)>>,
|
||||
}
|
||||
|
||||
impl<V: View> Hook<V> {
|
||||
impl<V: 'static> Hook<V> {
|
||||
pub fn new(child: impl Element<V>) -> Self {
|
||||
Self {
|
||||
child: child.into_any(),
|
||||
@ -29,7 +28,7 @@ impl<V: View> Hook<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Hook<V> {
|
||||
impl<V: 'static> Element<V> for Hook<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
},
|
||||
json::{json, ToJson},
|
||||
scene, Border, Element, ImageData, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
View, ViewContext,
|
||||
ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -57,7 +57,7 @@ impl Image {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Image {
|
||||
impl<V: 'static> Element<V> for Image {
|
||||
type LayoutState = Option<Arc<ImageData>>;
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -31,7 +31,7 @@ impl KeystrokeLabel {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for KeystrokeLabel {
|
||||
impl<V: 'static> Element<V> for KeystrokeLabel {
|
||||
type LayoutState = AnyElement<V>;
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle},
|
||||
Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -128,7 +128,7 @@ impl Label {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Label {
|
||||
impl<V: 'static> Element<V> for Label {
|
||||
type LayoutState = Line;
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -5,16 +5,16 @@ use crate::{
|
||||
},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, SizeConstraint,
|
||||
View, ViewContext,
|
||||
ViewContext,
|
||||
};
|
||||
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
|
||||
use sum_tree::{Bias, SumTree};
|
||||
|
||||
pub struct List<V: View> {
|
||||
pub struct List<V> {
|
||||
state: ListState<V>,
|
||||
}
|
||||
|
||||
pub struct ListState<V: View>(Rc<RefCell<StateInner<V>>>);
|
||||
pub struct ListState<V>(Rc<RefCell<StateInner<V>>>);
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Orientation {
|
||||
@ -22,7 +22,7 @@ pub enum Orientation {
|
||||
Bottom,
|
||||
}
|
||||
|
||||
struct StateInner<V: View> {
|
||||
struct StateInner<V> {
|
||||
last_layout_width: Option<f32>,
|
||||
render_item: Box<dyn FnMut(&mut V, usize, &mut ViewContext<V>) -> AnyElement<V>>,
|
||||
rendered_range: Range<usize>,
|
||||
@ -40,13 +40,13 @@ pub struct ListOffset {
|
||||
pub offset_in_item: f32,
|
||||
}
|
||||
|
||||
enum ListItem<V: View> {
|
||||
enum ListItem<V> {
|
||||
Unrendered,
|
||||
Rendered(Rc<RefCell<AnyElement<V>>>),
|
||||
Removed(f32),
|
||||
}
|
||||
|
||||
impl<V: View> Clone for ListItem<V> {
|
||||
impl<V> Clone for ListItem<V> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Unrendered => Self::Unrendered,
|
||||
@ -56,7 +56,7 @@ impl<V: View> Clone for ListItem<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Debug for ListItem<V> {
|
||||
impl<V> Debug for ListItem<V> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Unrendered => write!(f, "Unrendered"),
|
||||
@ -86,13 +86,13 @@ struct UnrenderedCount(usize);
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct Height(f32);
|
||||
|
||||
impl<V: View> List<V> {
|
||||
impl<V> List<V> {
|
||||
pub fn new(state: ListState<V>) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for List<V> {
|
||||
impl<V: 'static> Element<V> for List<V> {
|
||||
type LayoutState = ListOffset;
|
||||
type PaintState = ();
|
||||
|
||||
@ -347,7 +347,7 @@ impl<V: View> Element<V> for List<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> ListState<V> {
|
||||
impl<V: 'static> ListState<V> {
|
||||
pub fn new<D, F>(
|
||||
element_count: usize,
|
||||
orientation: Orientation,
|
||||
@ -440,13 +440,13 @@ impl<V: View> ListState<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Clone for ListState<V> {
|
||||
impl<V> Clone for ListState<V> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> StateInner<V> {
|
||||
impl<V: 'static> StateInner<V> {
|
||||
fn render_item(
|
||||
&mut self,
|
||||
ix: usize,
|
||||
@ -560,7 +560,7 @@ impl<V: View> StateInner<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> ListItem<V> {
|
||||
impl<V> ListItem<V> {
|
||||
fn remove(&self) -> Self {
|
||||
match self {
|
||||
ListItem::Unrendered => ListItem::Unrendered,
|
||||
@ -570,7 +570,7 @@ impl<V: View> ListItem<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> sum_tree::Item for ListItem<V> {
|
||||
impl<V> sum_tree::Item for ListItem<V> {
|
||||
type Summary = ListItemSummary;
|
||||
|
||||
fn summary(&self) -> Self::Summary {
|
||||
@ -944,7 +944,7 @@ mod tests {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for TestView {
|
||||
impl crate::View for TestView {
|
||||
fn ui_name() -> &'static str {
|
||||
"TestView"
|
||||
}
|
||||
@ -968,7 +968,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for TestElement {
|
||||
impl<V: 'static> Element<V> for TestElement {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -11,12 +11,12 @@ use crate::{
|
||||
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||
},
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
|
||||
SceneBuilder, SizeConstraint, TypeTag, View, ViewContext,
|
||||
SceneBuilder, SizeConstraint, TypeTag, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::ops::Range;
|
||||
|
||||
pub struct MouseEventHandler<V: View> {
|
||||
pub struct MouseEventHandler<V: 'static> {
|
||||
child: AnyElement<V>,
|
||||
region_id: usize,
|
||||
cursor_style: Option<CursorStyle>,
|
||||
@ -31,7 +31,7 @@ pub struct MouseEventHandler<V: View> {
|
||||
|
||||
/// Element which provides a render_child callback with a MouseState and paints a mouse
|
||||
/// region under (or above) it for easy mouse event handling.
|
||||
impl<V: View> MouseEventHandler<V> {
|
||||
impl<V: 'static> MouseEventHandler<V> {
|
||||
pub fn for_child<Tag: 'static>(child: impl Element<V>, region_id: usize) -> Self {
|
||||
Self {
|
||||
child: child.into_any(),
|
||||
@ -267,7 +267,7 @@ impl<V: View> MouseEventHandler<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for MouseEventHandler<V> {
|
||||
impl<V: 'static> Element<V> for MouseEventHandler<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -4,11 +4,11 @@ use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::ToJson,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, View, ViewContext,
|
||||
SizeConstraint, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
pub struct Overlay<V: View> {
|
||||
pub struct Overlay<V> {
|
||||
child: AnyElement<V>,
|
||||
anchor_position: Option<Vector2F>,
|
||||
anchor_corner: AnchorCorner,
|
||||
@ -73,7 +73,7 @@ impl AnchorCorner {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Overlay<V> {
|
||||
impl<V: 'static> Overlay<V> {
|
||||
pub fn new(child: impl Element<V>) -> Self {
|
||||
Self {
|
||||
child: child.into_any(),
|
||||
@ -117,7 +117,7 @@ impl<V: View> Overlay<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Overlay<V> {
|
||||
impl<V: 'static> Element<V> for Overlay<V> {
|
||||
type LayoutState = Vector2F;
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -59,7 +59,7 @@ where
|
||||
.and_then(|map| map.0.get(&tag))
|
||||
}
|
||||
|
||||
pub struct Resizable<V: View> {
|
||||
pub struct Resizable<V: 'static> {
|
||||
child: AnyElement<V>,
|
||||
tag: TypeTag,
|
||||
handle_side: HandleSide,
|
||||
@ -69,7 +69,7 @@ pub struct Resizable<V: View> {
|
||||
|
||||
const DEFAULT_HANDLE_SIZE: f32 = 4.0;
|
||||
|
||||
impl<V: View> Resizable<V> {
|
||||
impl<V: 'static> Resizable<V> {
|
||||
pub fn new<Tag: 'static>(
|
||||
child: AnyElement<V>,
|
||||
handle_side: HandleSide,
|
||||
@ -97,7 +97,7 @@ impl<V: View> Resizable<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Resizable<V> {
|
||||
impl<V: 'static> Element<V> for Resizable<V> {
|
||||
type LayoutState = SizeConstraint;
|
||||
type PaintState = ();
|
||||
|
||||
@ -219,12 +219,12 @@ impl<V: View> Element<V> for Resizable<V> {
|
||||
#[derive(Debug, Default)]
|
||||
struct ProviderMap(HashMap<TypeTag, (RectF, RectF)>);
|
||||
|
||||
pub struct BoundsProvider<V: View, P> {
|
||||
pub struct BoundsProvider<V: 'static, P> {
|
||||
child: AnyElement<V>,
|
||||
phantom: std::marker::PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<V: View, P: 'static> BoundsProvider<V, P> {
|
||||
impl<V: 'static, P: 'static> BoundsProvider<V, P> {
|
||||
pub fn new(child: AnyElement<V>) -> Self {
|
||||
Self {
|
||||
child,
|
||||
|
@ -3,17 +3,16 @@ use std::ops::Range;
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::{self, json, ToJson},
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
};
|
||||
|
||||
/// Element which renders it's children in a stack on top of each other.
|
||||
/// The first child determines the size of the others.
|
||||
pub struct Stack<V: View> {
|
||||
pub struct Stack<V> {
|
||||
children: Vec<AnyElement<V>>,
|
||||
}
|
||||
|
||||
impl<V: View> Default for Stack<V> {
|
||||
impl<V> Default for Stack<V> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
children: Vec::new(),
|
||||
@ -21,13 +20,13 @@ impl<V: View> Default for Stack<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Stack<V> {
|
||||
impl<V> Stack<V> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Stack<V> {
|
||||
impl<V: 'static> Element<V> for Stack<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
@ -99,7 +98,7 @@ impl<V: View> Element<V> for Stack<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Extend<AnyElement<V>> for Stack<V> {
|
||||
impl<V> Extend<AnyElement<V>> for Stack<V> {
|
||||
fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
|
||||
self.children.extend(children)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::Deserialize;
|
||||
@ -27,7 +27,7 @@ impl Svg {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_style<V: View>(style: SvgStyle) -> impl Element<V> {
|
||||
pub fn for_style<V: 'static>(style: SvgStyle) -> impl Element<V> {
|
||||
Self::new(style.asset)
|
||||
.with_color(style.color)
|
||||
.constrained()
|
||||
@ -41,7 +41,7 @@ impl Svg {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Svg {
|
||||
impl<V: 'static> Element<V> for Svg {
|
||||
type LayoutState = Option<usvg::Tree>;
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle, ShapedBoundary},
|
||||
AppContext, Element, FontCache, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
TextLayoutCache, View, ViewContext,
|
||||
TextLayoutCache, ViewContext,
|
||||
};
|
||||
use log::warn;
|
||||
use serde_json::json;
|
||||
@ -70,7 +70,7 @@ impl Text {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Text {
|
||||
impl<V: 'static> Element<V> for Text {
|
||||
type LayoutState = LayoutState;
|
||||
type PaintState = ();
|
||||
|
||||
@ -338,7 +338,7 @@ impl<V: View> Element<V> for Text {
|
||||
}
|
||||
|
||||
/// Perform text layout on a series of highlighted chunks of text.
|
||||
fn layout_highlighted_chunks<'a>(
|
||||
pub fn layout_highlighted_chunks<'a>(
|
||||
chunks: impl Iterator<Item = (&'a str, Option<HighlightStyle>)>,
|
||||
text_style: &TextStyle,
|
||||
text_layout_cache: &TextLayoutCache,
|
||||
|
@ -7,7 +7,7 @@ use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
Task, TypeTag, View, ViewContext,
|
||||
Task, TypeTag, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
@ -22,7 +22,7 @@ use util::ResultExt;
|
||||
|
||||
const DEBOUNCE_TIMEOUT: Duration = Duration::from_millis(500);
|
||||
|
||||
pub struct Tooltip<V: View> {
|
||||
pub struct Tooltip<V> {
|
||||
child: AnyElement<V>,
|
||||
tooltip: Option<AnyElement<V>>,
|
||||
_state: ElementStateHandle<Rc<TooltipState>>,
|
||||
@ -52,7 +52,7 @@ pub struct KeystrokeStyle {
|
||||
text: TextStyle,
|
||||
}
|
||||
|
||||
impl<V: View> Tooltip<V> {
|
||||
impl<V: 'static> Tooltip<V> {
|
||||
pub fn new<Tag: 'static>(
|
||||
id: usize,
|
||||
text: impl Into<Cow<'static, str>>,
|
||||
@ -181,7 +181,7 @@ impl<V: View> Tooltip<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for Tooltip<V> {
|
||||
impl<V: 'static> Element<V> for Tooltip<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
},
|
||||
json::{self, json},
|
||||
platform::ScrollWheelEvent,
|
||||
AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, View, ViewContext,
|
||||
AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
@ -36,13 +36,13 @@ struct StateInner {
|
||||
scroll_to: Option<ScrollTarget>,
|
||||
}
|
||||
|
||||
pub struct UniformListLayoutState<V: View> {
|
||||
pub struct UniformListLayoutState<V> {
|
||||
scroll_max: f32,
|
||||
item_height: f32,
|
||||
items: Vec<AnyElement<V>>,
|
||||
}
|
||||
|
||||
pub struct UniformList<V: View> {
|
||||
pub struct UniformList<V> {
|
||||
state: UniformListState,
|
||||
item_count: usize,
|
||||
#[allow(clippy::type_complexity)]
|
||||
@ -53,7 +53,7 @@ pub struct UniformList<V: View> {
|
||||
view_id: usize,
|
||||
}
|
||||
|
||||
impl<V: View> UniformList<V> {
|
||||
impl<V: 'static> UniformList<V> {
|
||||
pub fn new<F>(
|
||||
state: UniformListState,
|
||||
item_count: usize,
|
||||
@ -61,7 +61,6 @@ impl<V: View> UniformList<V> {
|
||||
append_items: F,
|
||||
) -> Self
|
||||
where
|
||||
V: View,
|
||||
F: 'static + Fn(&mut V, Range<usize>, &mut Vec<AnyElement<V>>, &mut ViewContext<V>),
|
||||
{
|
||||
Self {
|
||||
@ -151,7 +150,7 @@ impl<V: View> UniformList<V> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Element<V> for UniformList<V> {
|
||||
impl<V: 'static> Element<V> for UniformList<V> {
|
||||
type LayoutState = UniformListLayoutState<V>;
|
||||
type PaintState = ();
|
||||
|
||||
|
@ -11,6 +11,7 @@ pub use font_kit::{
|
||||
properties::{Properties, Stretch, Style, Weight},
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
use refineable::Refineable;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{de, Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@ -59,7 +60,7 @@ pub struct Features {
|
||||
pub zero: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, JsonSchema)]
|
||||
#[derive(Clone, Debug, JsonSchema, Refineable)]
|
||||
pub struct TextStyle {
|
||||
pub color: Color,
|
||||
pub font_family_name: Arc<str>,
|
||||
@ -69,6 +70,7 @@ pub struct TextStyle {
|
||||
#[schemars(with = "PropertiesDef")]
|
||||
pub font_properties: Properties,
|
||||
pub underline: Underline,
|
||||
pub soft_wrap: bool,
|
||||
}
|
||||
|
||||
impl TextStyle {
|
||||
@ -90,20 +92,11 @@ impl TextStyle {
|
||||
font_size: refinement.font_size.unwrap_or(self.font_size),
|
||||
font_properties: refinement.font_properties.unwrap_or(self.font_properties),
|
||||
underline: refinement.underline.unwrap_or(self.underline),
|
||||
soft_wrap: refinement.soft_wrap.unwrap_or(self.soft_wrap),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextStyleRefinement {
|
||||
pub color: Option<Color>,
|
||||
pub font_family_name: Option<Arc<str>>,
|
||||
pub font_family_id: Option<FamilyId>,
|
||||
pub font_id: Option<FontId>,
|
||||
pub font_size: Option<f32>,
|
||||
pub font_properties: Option<Properties>,
|
||||
pub underline: Option<Underline>,
|
||||
}
|
||||
|
||||
#[derive(JsonSchema)]
|
||||
#[serde(remote = "Properties")]
|
||||
pub struct PropertiesDef {
|
||||
@ -222,9 +215,31 @@ impl TextStyle {
|
||||
font_size,
|
||||
font_properties,
|
||||
underline,
|
||||
soft_wrap: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn default(font_cache: &FontCache) -> Self {
|
||||
let font_family_id = font_cache.known_existing_family();
|
||||
let font_id = font_cache
|
||||
.select_font(font_family_id, &Default::default())
|
||||
.expect("did not have any font in system-provided family");
|
||||
let font_family_name = font_cache
|
||||
.family_name(font_family_id)
|
||||
.expect("we loaded this family from the font cache, so this should work");
|
||||
|
||||
Self {
|
||||
color: Color::default(),
|
||||
font_family_name,
|
||||
font_family_id,
|
||||
font_id,
|
||||
font_size: 14.,
|
||||
font_properties: Default::default(),
|
||||
underline: Default::default(),
|
||||
soft_wrap: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_font_size(mut self, font_size: f32) -> Self {
|
||||
self.font_size = font_size;
|
||||
self
|
||||
@ -352,24 +367,7 @@ impl Default for TextStyle {
|
||||
let font_cache = font_cache
|
||||
.as_ref()
|
||||
.expect("TextStyle::default can only be called within a call to with_font_cache");
|
||||
|
||||
let font_family_id = font_cache.known_existing_family();
|
||||
let font_id = font_cache
|
||||
.select_font(font_family_id, &Default::default())
|
||||
.expect("did not have any font in system-provided family");
|
||||
let font_family_name = font_cache
|
||||
.family_name(font_family_id)
|
||||
.expect("we loaded this family from the font cache, so this should work");
|
||||
|
||||
Self {
|
||||
color: Default::default(),
|
||||
font_family_name,
|
||||
font_family_id,
|
||||
font_id,
|
||||
font_size: 14.,
|
||||
font_properties: Default::default(),
|
||||
underline: Default::default(),
|
||||
}
|
||||
Self::default(font_cache)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use super::scene::{Path, PathVertex};
|
||||
use crate::{color::Color, json::ToJson};
|
||||
pub use pathfinder_geometry::*;
|
||||
use rect::RectF;
|
||||
use refineable::Refineable;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde_json::json;
|
||||
use vector::{vec2f, Vector2F};
|
||||
@ -131,3 +132,258 @@ impl ToJson for RectF {
|
||||
json!({"origin": self.origin().to_json(), "size": self.size().to_json()})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Refineable)]
|
||||
pub struct Point<T: Clone + Default> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
impl<T: Clone + Default> Clone for Point<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
|
||||
fn into(self) -> taffy::geometry::Point<T> {
|
||||
taffy::geometry::Point {
|
||||
x: self.x,
|
||||
y: self.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Refineable)]
|
||||
pub struct Size<T: Clone + Default> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
}
|
||||
|
||||
impl<S, T: Clone + Default> From<taffy::geometry::Size<S>> for Size<T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
fn from(value: taffy::geometry::Size<S>) -> Self {
|
||||
Self {
|
||||
width: value.width.into(),
|
||||
height: value.height.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T: Clone + Default> Into<taffy::geometry::Size<S>> for Size<T>
|
||||
where
|
||||
T: Into<S>,
|
||||
{
|
||||
fn into(self) -> taffy::geometry::Size<S> {
|
||||
taffy::geometry::Size {
|
||||
width: self.width.into(),
|
||||
height: self.height.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Size<DefiniteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
width: pixels(0.),
|
||||
height: pixels(0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Size<taffy::style::LengthPercentage> {
|
||||
taffy::geometry::Size {
|
||||
width: self.width.to_taffy(rem_size),
|
||||
height: self.height.to_taffy(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Size<Length> {
|
||||
pub fn auto() -> Self {
|
||||
Self {
|
||||
width: Length::Auto,
|
||||
height: Length::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy<T: From<taffy::prelude::LengthPercentageAuto>>(
|
||||
&self,
|
||||
rem_size: f32,
|
||||
) -> taffy::geometry::Size<T> {
|
||||
taffy::geometry::Size {
|
||||
width: self.width.to_taffy(rem_size).into(),
|
||||
height: self.height.to_taffy(rem_size).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Refineable)]
|
||||
pub struct Edges<T: Clone + Default> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
|
||||
impl Edges<DefiniteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: pixels(0.),
|
||||
right: pixels(0.),
|
||||
bottom: pixels(0.),
|
||||
left: pixels(0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
|
||||
taffy::geometry::Rect {
|
||||
top: self.top.to_taffy(rem_size),
|
||||
right: self.right.to_taffy(rem_size),
|
||||
bottom: self.bottom.to_taffy(rem_size),
|
||||
left: self.left.to_taffy(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edges<Length> {
|
||||
pub fn auto() -> Self {
|
||||
Self {
|
||||
top: Length::Auto,
|
||||
right: Length::Auto,
|
||||
bottom: Length::Auto,
|
||||
left: Length::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: pixels(0.),
|
||||
right: pixels(0.),
|
||||
bottom: pixels(0.),
|
||||
left: pixels(0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy(
|
||||
&self,
|
||||
rem_size: f32,
|
||||
) -> taffy::geometry::Rect<taffy::style::LengthPercentageAuto> {
|
||||
taffy::geometry::Rect {
|
||||
top: self.top.to_taffy(rem_size),
|
||||
right: self.right.to_taffy(rem_size),
|
||||
bottom: self.bottom.to_taffy(rem_size),
|
||||
left: self.left.to_taffy(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AbsoluteLength {
|
||||
Pixels(f32),
|
||||
Rems(f32),
|
||||
}
|
||||
|
||||
impl AbsoluteLength {
|
||||
pub fn to_pixels(&self, rem_size: f32) -> f32 {
|
||||
match self {
|
||||
AbsoluteLength::Pixels(pixels) => *pixels,
|
||||
AbsoluteLength::Rems(rems) => rems * rem_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AbsoluteLength {
|
||||
fn default() -> Self {
|
||||
Self::Pixels(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A non-auto length that can be defined in pixels, rems, or percent of parent.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DefiniteLength {
|
||||
Absolute(AbsoluteLength),
|
||||
Relative(f32), // Percent, from 0 to 100.
|
||||
}
|
||||
|
||||
impl DefiniteLength {
|
||||
fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
|
||||
match self {
|
||||
DefiniteLength::Absolute(length) => match length {
|
||||
AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
|
||||
AbsoluteLength::Rems(rems) => {
|
||||
taffy::style::LengthPercentage::Length(rems * rem_size)
|
||||
}
|
||||
},
|
||||
DefiniteLength::Relative(fraction) => {
|
||||
taffy::style::LengthPercentage::Percent(*fraction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AbsoluteLength> for DefiniteLength {
|
||||
fn from(length: AbsoluteLength) -> Self {
|
||||
Self::Absolute(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DefiniteLength {
|
||||
fn default() -> Self {
|
||||
Self::Absolute(AbsoluteLength::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// A length that can be defined in pixels, rems, percent of parent, or auto.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Length {
|
||||
Definite(DefiniteLength),
|
||||
Auto,
|
||||
}
|
||||
|
||||
pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
|
||||
DefiniteLength::Relative(fraction).into()
|
||||
}
|
||||
|
||||
pub fn rems<T: From<AbsoluteLength>>(rems: f32) -> T {
|
||||
AbsoluteLength::Rems(rems).into()
|
||||
}
|
||||
|
||||
pub fn pixels<T: From<AbsoluteLength>>(pixels: f32) -> T {
|
||||
AbsoluteLength::Pixels(pixels).into()
|
||||
}
|
||||
|
||||
pub fn auto() -> Length {
|
||||
Length::Auto
|
||||
}
|
||||
|
||||
impl Length {
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::prelude::LengthPercentageAuto {
|
||||
match self {
|
||||
Length::Definite(length) => length.to_taffy(rem_size).into(),
|
||||
Length::Auto => taffy::prelude::LengthPercentageAuto::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DefiniteLength> for Length {
|
||||
fn from(length: DefiniteLength) -> Self {
|
||||
Self::Definite(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AbsoluteLength> for Length {
|
||||
fn from(length: AbsoluteLength) -> Self {
|
||||
Self::Definite(length.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Length {
|
||||
fn default() -> Self {
|
||||
Self::Definite(DefiniteLength::default())
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,10 @@ pub mod json;
|
||||
pub mod keymap_matcher;
|
||||
pub mod platform;
|
||||
pub use gpui_macros::{test, Element};
|
||||
pub use window::{Axis, RectFExt, SizeConstraint, Vector2FExt, WindowContext};
|
||||
pub use window::{
|
||||
Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt,
|
||||
WindowContext,
|
||||
};
|
||||
|
||||
pub use anyhow;
|
||||
pub use serde_json;
|
||||
|
@ -192,7 +192,7 @@ impl<'a> WindowOptions<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TitlebarOptions<'a> {
|
||||
pub title: Option<&'a str>,
|
||||
pub appears_transparent: bool,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::ops::Deref;
|
||||
use std::{any::Any, ops::Deref};
|
||||
|
||||
use pathfinder_geometry::vector::vec2f;
|
||||
|
||||
@ -142,6 +142,7 @@ pub struct MouseButtonEvent {
|
||||
pub position: Vector2F,
|
||||
pub modifiers: Modifiers,
|
||||
pub click_count: usize,
|
||||
pub is_down: bool,
|
||||
}
|
||||
|
||||
impl Deref for MouseButtonEvent {
|
||||
@ -174,6 +175,7 @@ impl MouseMovedEvent {
|
||||
button: self.pressed_button.unwrap_or(button),
|
||||
modifiers: self.modifiers,
|
||||
click_count: 0,
|
||||
is_down: self.pressed_button.is_some(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,10 +213,24 @@ impl Event {
|
||||
Event::KeyDown { .. } => None,
|
||||
Event::KeyUp { .. } => None,
|
||||
Event::ModifiersChanged { .. } => None,
|
||||
Event::MouseDown(event) | Event::MouseUp(event) => Some(event.position),
|
||||
Event::MouseDown(event) => Some(event.position),
|
||||
Event::MouseUp(event) => Some(event.position),
|
||||
Event::MouseMoved(event) => Some(event.position),
|
||||
Event::MouseExited(event) => Some(event.position),
|
||||
Event::ScrollWheel(event) => Some(event.position),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
|
||||
match self {
|
||||
Event::KeyDown { .. } => None,
|
||||
Event::KeyUp { .. } => None,
|
||||
Event::ModifiersChanged { .. } => None,
|
||||
Event::MouseDown(event) => Some(event),
|
||||
Event::MouseUp(event) => Some(event),
|
||||
Event::MouseMoved(event) => Some(event),
|
||||
Event::MouseExited(event) => Some(event),
|
||||
Event::ScrollWheel(event) => Some(event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ impl Event {
|
||||
),
|
||||
modifiers: read_modifiers(native_event),
|
||||
click_count: native_event.clickCount() as usize,
|
||||
is_down: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -158,6 +159,7 @@ impl Event {
|
||||
),
|
||||
modifiers: read_modifiers(native_event),
|
||||
click_count: native_event.clickCount() as usize,
|
||||
is_down: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod mouse_event;
|
||||
mod mouse_region;
|
||||
mod region;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use collections::HashSet;
|
||||
@ -8,7 +9,12 @@ use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde_derive::Serialize;
|
||||
use serde_json::json;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::Cow,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
color::Color,
|
||||
@ -16,7 +22,7 @@ use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::ToJson,
|
||||
platform::{current::Surface, CursorStyle},
|
||||
ImageData,
|
||||
ImageData, WindowContext,
|
||||
};
|
||||
pub use mouse_event::*;
|
||||
pub use mouse_region::*;
|
||||
@ -25,6 +31,8 @@ pub struct SceneBuilder {
|
||||
scale_factor: f32,
|
||||
stacking_contexts: Vec<StackingContext>,
|
||||
active_stacking_context_stack: Vec<usize>,
|
||||
/// Used by the playground crate.
|
||||
pub event_handlers: Vec<EventHandler>,
|
||||
#[cfg(debug_assertions)]
|
||||
mouse_region_ids: HashSet<MouseRegionId>,
|
||||
}
|
||||
@ -32,6 +40,7 @@ pub struct SceneBuilder {
|
||||
pub struct Scene {
|
||||
scale_factor: f32,
|
||||
stacking_contexts: Vec<StackingContext>,
|
||||
event_handlers: Vec<EventHandler>,
|
||||
}
|
||||
|
||||
struct StackingContext {
|
||||
@ -272,6 +281,12 @@ impl Scene {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn take_event_handlers(&mut self) -> Vec<EventHandler> {
|
||||
self.event_handlers
|
||||
.sort_by(|a, b| a.order.cmp(&b.order).reverse());
|
||||
std::mem::take(&mut self.event_handlers)
|
||||
}
|
||||
}
|
||||
|
||||
impl SceneBuilder {
|
||||
@ -283,6 +298,7 @@ impl SceneBuilder {
|
||||
active_stacking_context_stack: vec![0],
|
||||
#[cfg(debug_assertions)]
|
||||
mouse_region_ids: Default::default(),
|
||||
event_handlers: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,6 +308,7 @@ impl SceneBuilder {
|
||||
Scene {
|
||||
scale_factor: self.scale_factor,
|
||||
stacking_contexts: self.stacking_contexts,
|
||||
event_handlers: self.event_handlers,
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,6 +705,13 @@ impl MouseRegion {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventHandler {
|
||||
pub order: u32,
|
||||
// The &dyn Any parameter below expects an event.
|
||||
pub handler: Rc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>,
|
||||
pub event_type: TypeId,
|
||||
}
|
||||
|
||||
fn can_draw(bounds: RectF) -> bool {
|
||||
let size = bounds.size();
|
||||
size.x() > 0. && size.y() > 0.
|
||||
|
@ -1,6 +1,4 @@
|
||||
use crate::{
|
||||
platform::MouseButton, window::WindowContext, EventContext, TypeTag, View, ViewContext,
|
||||
};
|
||||
use crate::{platform::MouseButton, window::WindowContext, EventContext, TypeTag, ViewContext};
|
||||
use collections::HashMap;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use smallvec::SmallVec;
|
||||
@ -72,7 +70,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_down(button, handler);
|
||||
@ -81,7 +79,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_up(button, handler);
|
||||
@ -90,7 +88,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_click(button, handler);
|
||||
@ -99,7 +97,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_click_out(button, handler);
|
||||
@ -108,7 +106,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_down_out(button, handler);
|
||||
@ -117,7 +115,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_up_out(button, handler);
|
||||
@ -126,7 +124,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_drag(button, handler);
|
||||
@ -135,7 +133,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_hover<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_hover(handler);
|
||||
@ -144,7 +142,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_move<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_move(handler);
|
||||
@ -153,7 +151,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_move_out<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_move_out(handler);
|
||||
@ -162,7 +160,7 @@ impl MouseRegion {
|
||||
|
||||
pub fn on_scroll<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.handlers = self.handlers.on_scroll(handler);
|
||||
@ -314,7 +312,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_move<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseMove, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::move_disc(), None,
|
||||
@ -336,7 +334,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_move_out<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseMoveOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::move_out_disc(), None,
|
||||
@ -358,7 +356,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_down<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseDown, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::down_disc(), Some(button),
|
||||
@ -380,7 +378,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_up<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseUp, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::up_disc(), Some(button),
|
||||
@ -402,7 +400,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_click<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseClick, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::click_disc(), Some(button),
|
||||
@ -424,7 +422,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::click_out_disc(), Some(button),
|
||||
@ -446,7 +444,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseDownOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::down_out_disc(), Some(button),
|
||||
@ -468,7 +466,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_up_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseUpOut, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::up_out_disc(), Some(button),
|
||||
@ -490,7 +488,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_drag<V, F>(mut self, button: MouseButton, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseDrag, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::drag_disc(), Some(button),
|
||||
@ -512,7 +510,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_hover<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseHover, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::hover_disc(), None,
|
||||
@ -534,7 +532,7 @@ impl HandlerSet {
|
||||
|
||||
pub fn on_scroll<V, F>(mut self, handler: F) -> Self
|
||||
where
|
||||
V: View,
|
||||
V: 'static,
|
||||
F: Fn(MouseScrollWheel, &mut V, &mut EventContext<V>) + 'static,
|
||||
{
|
||||
self.insert(MouseEvent::scroll_wheel_disc(), None,
|
||||
|
7
crates/gpui/src/scene/region.rs
Normal file
7
crates/gpui/src/scene/region.rs
Normal file
@ -0,0 +1,7 @@
|
||||
// use crate::geometry::rect::RectF;
|
||||
// use crate::WindowContext;
|
||||
|
||||
// struct Region {
|
||||
// pub bounds: RectF,
|
||||
// pub click_handler: Option<Rc<dyn Fn(&dyn Any, MouseEvent, &mut WindowContext)>>,
|
||||
// }
|
@ -212,7 +212,7 @@ pub struct Glyph {
|
||||
}
|
||||
|
||||
impl Line {
|
||||
fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
|
||||
pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
|
||||
let mut style_runs = SmallVec::new();
|
||||
for (len, style) in runs {
|
||||
style_runs.push(StyleRun {
|
||||
|
@ -1,14 +1,14 @@
|
||||
use gpui::{elements::RenderElement, View, ViewContext};
|
||||
use gpui_macros::Element;
|
||||
use gpui::{elements::Empty, Element, ViewContext};
|
||||
// use gpui_macros::Element;
|
||||
|
||||
#[test]
|
||||
fn test_derive_render_element() {
|
||||
#[derive(Element)]
|
||||
struct TestElement {}
|
||||
|
||||
impl RenderElement for TestElement {
|
||||
fn render<V: View>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> gpui::AnyElement<V> {
|
||||
unimplemented!()
|
||||
impl TestElement {
|
||||
fn render<V: 'static>(&mut self, _: &mut V, _: &mut ViewContext<V>) -> impl Element<V> {
|
||||
Empty::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ proc-macro = true
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
lazy_static.workspace = true
|
||||
proc-macro2 = "1.0"
|
||||
syn = "1.0"
|
||||
quote = "1.0"
|
||||
proc-macro2 = "1.0"
|
||||
|
@ -4,7 +4,7 @@ use quote::{format_ident, quote};
|
||||
use std::mem;
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, DeriveInput, FnArg,
|
||||
ItemFn, Lit, Meta, NestedMeta, Type,
|
||||
GenericParam, Generics, ItemFn, Lit, Meta, NestedMeta, Type, WhereClause,
|
||||
};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
@ -278,18 +278,44 @@ fn parse_bool(literal: &Lit) -> Result<bool, TokenStream> {
|
||||
|
||||
#[proc_macro_derive(Element)]
|
||||
pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||
// Parse the input tokens into a syntax tree
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let type_name = ast.ident;
|
||||
|
||||
// The name of the struct/enum
|
||||
let name = input.ident;
|
||||
let must_implement = format_ident!("{}MustImplementRenderElement", name);
|
||||
let placeholder_view_generics: Generics = parse_quote! { <V: 'static> };
|
||||
let placeholder_view_type_name: Ident = parse_quote! { V };
|
||||
let view_type_name: Ident;
|
||||
let impl_generics: syn::ImplGenerics<'_>;
|
||||
let type_generics: Option<syn::TypeGenerics<'_>>;
|
||||
let where_clause: Option<&'_ WhereClause>;
|
||||
|
||||
let expanded = quote! {
|
||||
trait #must_implement : gpui::elements::RenderElement {}
|
||||
impl #must_implement for #name {}
|
||||
match ast.generics.params.iter().find_map(|param| {
|
||||
if let GenericParam::Type(type_param) = param {
|
||||
Some(type_param.ident.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some(type_name) => {
|
||||
view_type_name = type_name;
|
||||
let generics = ast.generics.split_for_impl();
|
||||
impl_generics = generics.0;
|
||||
type_generics = Some(generics.1);
|
||||
where_clause = generics.2;
|
||||
}
|
||||
_ => {
|
||||
view_type_name = placeholder_view_type_name;
|
||||
let generics = placeholder_view_generics.split_for_impl();
|
||||
impl_generics = generics.0;
|
||||
type_generics = None;
|
||||
where_clause = generics.2;
|
||||
}
|
||||
}
|
||||
|
||||
let gen = quote! {
|
||||
impl #impl_generics Element<#view_type_name> for #type_name #type_generics
|
||||
#where_clause
|
||||
{
|
||||
|
||||
impl<V: gpui::View> gpui::elements::Element<V> for #name {
|
||||
type LayoutState = gpui::elements::AnyElement<V>;
|
||||
type PaintState = ();
|
||||
|
||||
@ -299,7 +325,7 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||
view: &mut V,
|
||||
cx: &mut gpui::LayoutContext<V>,
|
||||
) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) {
|
||||
let mut element = self.render(view, cx);
|
||||
let mut element = self.render(view, cx).into_any();
|
||||
let size = element.layout(constraint, view, cx);
|
||||
(size, element)
|
||||
}
|
||||
@ -336,11 +362,11 @@ pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||
_: &(),
|
||||
view: &V,
|
||||
cx: &gpui::ViewContext<V>,
|
||||
) -> gpui::serde_json::Value {
|
||||
) -> gpui::json::Value {
|
||||
element.debug(view, cx)
|
||||
}
|
||||
}
|
||||
};
|
||||
// Return generated code
|
||||
TokenStream::from(expanded)
|
||||
|
||||
gen.into()
|
||||
}
|
||||
|
@ -2192,13 +2192,16 @@ impl BufferSnapshot {
|
||||
let mut end = start;
|
||||
let mut next_chars = self.chars_at(start).peekable();
|
||||
let mut prev_chars = self.reversed_chars_at(start).peekable();
|
||||
|
||||
let language = self.language_at(start);
|
||||
let kind = |c| char_kind(language, c);
|
||||
let word_kind = cmp::max(
|
||||
prev_chars.peek().copied().map(char_kind),
|
||||
next_chars.peek().copied().map(char_kind),
|
||||
prev_chars.peek().copied().map(kind),
|
||||
next_chars.peek().copied().map(kind),
|
||||
);
|
||||
|
||||
for ch in prev_chars {
|
||||
if Some(char_kind(ch)) == word_kind && ch != '\n' {
|
||||
if Some(kind(ch)) == word_kind && ch != '\n' {
|
||||
start -= ch.len_utf8();
|
||||
} else {
|
||||
break;
|
||||
@ -2206,7 +2209,7 @@ impl BufferSnapshot {
|
||||
}
|
||||
|
||||
for ch in next_chars {
|
||||
if Some(char_kind(ch)) == word_kind && ch != '\n' {
|
||||
if Some(kind(ch)) == word_kind && ch != '\n' {
|
||||
end += ch.len_utf8();
|
||||
} else {
|
||||
break;
|
||||
@ -3003,14 +3006,18 @@ pub fn contiguous_ranges(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn char_kind(c: char) -> CharKind {
|
||||
pub fn char_kind(language: Option<&Arc<Language>>, c: char) -> CharKind {
|
||||
if c.is_whitespace() {
|
||||
CharKind::Whitespace
|
||||
return CharKind::Whitespace;
|
||||
} else if c.is_alphanumeric() || c == '_' {
|
||||
CharKind::Word
|
||||
} else {
|
||||
CharKind::Punctuation
|
||||
return CharKind::Word;
|
||||
}
|
||||
if let Some(language) = language {
|
||||
if language.config.word_characters.contains(&c) {
|
||||
return CharKind::Word;
|
||||
}
|
||||
}
|
||||
CharKind::Punctuation
|
||||
}
|
||||
|
||||
/// Find all of the ranges of whitespace that occur at the ends of lines
|
||||
|
@ -11,7 +11,7 @@ mod buffer_tests;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use collections::{HashMap, HashSet};
|
||||
use futures::{
|
||||
channel::oneshot,
|
||||
future::{BoxFuture, Shared},
|
||||
@ -344,6 +344,8 @@ pub struct LanguageConfig {
|
||||
pub block_comment: Option<(Arc<str>, Arc<str>)>,
|
||||
#[serde(default)]
|
||||
pub overrides: HashMap<String, LanguageConfigOverride>,
|
||||
#[serde(default)]
|
||||
pub word_characters: HashSet<char>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -411,6 +413,7 @@ impl Default for LanguageConfig {
|
||||
block_comment: Default::default(),
|
||||
overrides: Default::default(),
|
||||
collapsed_placeholder: Default::default(),
|
||||
word_characters: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ impl View for LspLogView {
|
||||
}
|
||||
|
||||
impl Item for LspLogView {
|
||||
fn tab_content<V: View>(
|
||||
fn tab_content<V: 'static>(
|
||||
&self,
|
||||
_: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
|
@ -373,6 +373,7 @@ impl View for SyntaxTreeView {
|
||||
font_size,
|
||||
font_properties: Default::default(),
|
||||
underline: Default::default(),
|
||||
soft_wrap: false,
|
||||
};
|
||||
|
||||
let line_height = cx.font_cache().line_height(font_size);
|
||||
@ -451,7 +452,7 @@ impl View for SyntaxTreeView {
|
||||
}
|
||||
|
||||
impl Item for SyntaxTreeView {
|
||||
fn tab_content<V: View>(
|
||||
fn tab_content<V: 'static>(
|
||||
&self,
|
||||
_: Option<usize>,
|
||||
style: &theme::Tab,
|
||||
|
@ -5180,7 +5180,7 @@ impl Project {
|
||||
snapshot.file().map(|file| file.path().as_ref()),
|
||||
) {
|
||||
query
|
||||
.search(snapshot.as_rope())
|
||||
.search(&snapshot, None)
|
||||
.await
|
||||
.iter()
|
||||
.map(|range| {
|
||||
|
@ -3,7 +3,7 @@ use anyhow::{Context, Result};
|
||||
use client::proto;
|
||||
use globset::{Glob, GlobMatcher};
|
||||
use itertools::Itertools;
|
||||
use language::{char_kind, Rope};
|
||||
use language::{char_kind, BufferSnapshot};
|
||||
use regex::{Regex, RegexBuilder};
|
||||
use smol::future::yield_now;
|
||||
use std::{
|
||||
@ -39,6 +39,7 @@ pub enum SearchQuery {
|
||||
case_sensitive: bool,
|
||||
inner: SearchInputs,
|
||||
},
|
||||
|
||||
Regex {
|
||||
regex: Regex,
|
||||
|
||||
@ -214,12 +215,24 @@ impl SearchQuery {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn search(&self, rope: &Rope) -> Vec<Range<usize>> {
|
||||
pub async fn search(
|
||||
&self,
|
||||
buffer: &BufferSnapshot,
|
||||
subrange: Option<Range<usize>>,
|
||||
) -> Vec<Range<usize>> {
|
||||
const YIELD_INTERVAL: usize = 20000;
|
||||
|
||||
if self.as_str().is_empty() {
|
||||
return Default::default();
|
||||
}
|
||||
let language = buffer.language_at(0);
|
||||
let rope = if let Some(range) = subrange {
|
||||
buffer.as_rope().slice(range)
|
||||
} else {
|
||||
buffer.as_rope().clone()
|
||||
};
|
||||
|
||||
let kind = |c| char_kind(language, c);
|
||||
|
||||
let mut matches = Vec::new();
|
||||
match self {
|
||||
@ -236,10 +249,10 @@ impl SearchQuery {
|
||||
|
||||
let mat = mat.unwrap();
|
||||
if *whole_word {
|
||||
let prev_kind = rope.reversed_chars_at(mat.start()).next().map(char_kind);
|
||||
let start_kind = char_kind(rope.chars_at(mat.start()).next().unwrap());
|
||||
let end_kind = char_kind(rope.reversed_chars_at(mat.end()).next().unwrap());
|
||||
let next_kind = rope.chars_at(mat.end()).next().map(char_kind);
|
||||
let prev_kind = rope.reversed_chars_at(mat.start()).next().map(kind);
|
||||
let start_kind = kind(rope.chars_at(mat.start()).next().unwrap());
|
||||
let end_kind = kind(rope.reversed_chars_at(mat.end()).next().unwrap());
|
||||
let next_kind = rope.chars_at(mat.end()).next().map(kind);
|
||||
if Some(start_kind) == prev_kind || Some(end_kind) == next_kind {
|
||||
continue;
|
||||
}
|
||||
@ -247,6 +260,7 @@ impl SearchQuery {
|
||||
matches.push(mat.start()..mat.end())
|
||||
}
|
||||
}
|
||||
|
||||
Self::Regex {
|
||||
regex, multiline, ..
|
||||
} => {
|
||||
@ -284,6 +298,7 @@ impl SearchQuery {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches
|
||||
}
|
||||
|
||||
|
@ -1320,7 +1320,7 @@ impl ProjectPanel {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_entry_visual_element<V: View>(
|
||||
fn render_entry_visual_element<V: 'static>(
|
||||
details: &EntryDetails,
|
||||
editor: Option<&ViewHandle<Editor>>,
|
||||
padding: f32,
|
||||
|
@ -3,7 +3,7 @@ use std::path::Path;
|
||||
use fuzzy::StringMatch;
|
||||
use gpui::{
|
||||
elements::{Label, LabelStyle},
|
||||
AnyElement, Element, View,
|
||||
AnyElement, Element,
|
||||
};
|
||||
use util::paths::PathExt;
|
||||
use workspace::WorkspaceLocation;
|
||||
@ -43,7 +43,7 @@ impl HighlightedText {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render<V: View>(self, style: impl Into<LabelStyle>) -> AnyElement<V> {
|
||||
pub fn render<V: 'static>(self, style: impl Into<LabelStyle>) -> AnyElement<V> {
|
||||
Label::new(self.text, style)
|
||||
.with_highlights(self.highlight_positions)
|
||||
.into_any()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user