Compare commits
1 Commits
before-pai
...
markdown-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0649250a3 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -205,7 +205,6 @@ jobs:
|
||||
echo "invalid release tag ${GITHUB_REF_NAME}. expected ${expected_tag_name}"
|
||||
exit 1
|
||||
fi
|
||||
script/draft-release-notes "$version" "$channel" > target/release-notes.md
|
||||
|
||||
- name: Generate license file
|
||||
run: script/generate-licenses
|
||||
@@ -249,7 +248,7 @@ jobs:
|
||||
target/aarch64-apple-darwin/release/Zed-aarch64.dmg
|
||||
target/x86_64-apple-darwin/release/Zed-x86_64.dmg
|
||||
target/release/Zed.dmg
|
||||
body_file: target/release-notes.md
|
||||
body: ""
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
13
Cargo.lock
generated
13
Cargo.lock
generated
@@ -4327,7 +4327,6 @@ dependencies = [
|
||||
"time",
|
||||
"unindent",
|
||||
"url",
|
||||
"windows 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9737,6 +9736,7 @@ dependencies = [
|
||||
"file_icons",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"itertools 0.11.0",
|
||||
"language",
|
||||
"picker",
|
||||
"project",
|
||||
@@ -12522,7 +12522,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.133.0"
|
||||
version = "0.132.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
@@ -12699,13 +12699,6 @@ dependencies = [
|
||||
"zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_glsl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zed_haskell"
|
||||
version = "0.1.0"
|
||||
@@ -12743,7 +12736,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed_prisma"
|
||||
version = "0.0.2"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"zed_extension_api 0.0.4",
|
||||
]
|
||||
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -110,7 +110,6 @@ members = [
|
||||
"extensions/emmet",
|
||||
"extensions/erlang",
|
||||
"extensions/gleam",
|
||||
"extensions/glsl",
|
||||
"extensions/haskell",
|
||||
"extensions/html",
|
||||
"extensions/lua",
|
||||
@@ -260,9 +259,7 @@ futures-batch = "0.6.1"
|
||||
futures-lite = "1.13"
|
||||
git2 = { version = "0.18", default-features = false }
|
||||
globset = "0.4"
|
||||
heed = { git = "https://github.com/meilisearch/heed", rev = "036ac23f73a021894974b9adc815bc95b3e0482a", features = [
|
||||
"read-txn-no-tls",
|
||||
] }
|
||||
heed = { git = "https://github.com/meilisearch/heed", rev = "036ac23f73a021894974b9adc815bc95b3e0482a", features = ["read-txn-no-tls"] }
|
||||
hex = "0.4.3"
|
||||
ignore = "0.4.22"
|
||||
indoc = "1"
|
||||
@@ -367,16 +364,10 @@ sys-locale = "0.3.1"
|
||||
version = "0.53.0"
|
||||
features = [
|
||||
"implement",
|
||||
"Foundation_Numerics",
|
||||
"Wdk_System_SystemServices",
|
||||
"Win32_Globalization",
|
||||
"Win32_Graphics_Direct2D",
|
||||
"Win32_Graphics_Direct2D_Common",
|
||||
"Win32_Graphics_DirectWrite",
|
||||
"Win32_Graphics_Dxgi_Common",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Graphics_Imaging",
|
||||
"Win32_Graphics_Imaging_D2D",
|
||||
"Win32_Media",
|
||||
"Win32_Security",
|
||||
"Win32_Security_Credentials",
|
||||
|
||||
@@ -69,8 +69,6 @@
|
||||
"confirm_quit": false,
|
||||
// Whether to restore last closed project when fresh Zed instance is opened.
|
||||
"restore_on_startup": "last_workspace",
|
||||
// Size of the drop target in the editor.
|
||||
"drop_target_size": 0.2,
|
||||
// Whether the cursor blinks in the editor.
|
||||
"cursor_blink": true,
|
||||
// Whether to pop the completions menu while typing in an editor without
|
||||
@@ -214,8 +212,6 @@
|
||||
"scroll_debounce_ms": 50
|
||||
},
|
||||
"project_panel": {
|
||||
// Whether to show the project panel button in the status bar
|
||||
"button": true,
|
||||
// Default width of the project panel.
|
||||
"default_width": 240,
|
||||
// Where to dock the project panel. Can be 'left' or 'right'.
|
||||
|
||||
@@ -5,9 +5,6 @@ edition = "2021"
|
||||
publish = false
|
||||
license = "AGPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/anthropic.rs"
|
||||
|
||||
@@ -20,3 +17,6 @@ util.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tokio.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1094,37 +1094,31 @@ impl AssistantPanel {
|
||||
let view = cx.view().clone();
|
||||
let scroll_handle = self.saved_conversations_scroll_handle.clone();
|
||||
let conversation_count = self.saved_conversations.len();
|
||||
todo!("replace canvas")
|
||||
// canvas(
|
||||
// move |_, cx| {
|
||||
// let saved_conversations = uniform_list(
|
||||
// view.clone(),
|
||||
// "saved_conversations",
|
||||
// conversation_count,
|
||||
// |this, range, cx| {
|
||||
// range
|
||||
// .map(|ix| this.render_saved_conversation(ix, cx))
|
||||
// .collect()
|
||||
// },
|
||||
// )
|
||||
// .track_scroll(scroll_handle.clone())
|
||||
// .into_any_element();
|
||||
// saved_conversations.layout(absolute_offset, available_space, cx)
|
||||
// // compute layout for saved conversations
|
||||
// saved_conversations
|
||||
// },
|
||||
// move |bounds, saved_conversations, cx| {
|
||||
// saved_conversations.layout(
|
||||
// bounds.origin,
|
||||
// bounds.size.map(AvailableSpace::Definite),
|
||||
// cx,
|
||||
// );
|
||||
// saved_conversations
|
||||
// },
|
||||
// |_bounds, mut saved_conversations, cx| saved_conversations.paint(cx),
|
||||
// )
|
||||
// .size_full()
|
||||
// .into_any_element()
|
||||
canvas(
|
||||
move |bounds, cx| {
|
||||
let mut saved_conversations = uniform_list(
|
||||
view,
|
||||
"saved_conversations",
|
||||
conversation_count,
|
||||
|this, range, cx| {
|
||||
range
|
||||
.map(|ix| this.render_saved_conversation(ix, cx))
|
||||
.collect()
|
||||
},
|
||||
)
|
||||
.track_scroll(scroll_handle)
|
||||
.into_any_element();
|
||||
saved_conversations.layout(
|
||||
bounds.origin,
|
||||
bounds.size.map(AvailableSpace::Definite),
|
||||
cx,
|
||||
);
|
||||
saved_conversations
|
||||
},
|
||||
|_bounds, mut saved_conversations, cx| saved_conversations.paint(cx),
|
||||
)
|
||||
.size_full()
|
||||
.into_any_element()
|
||||
} else if let Some(editor) = self.active_conversation_editor() {
|
||||
let editor = editor.clone();
|
||||
let conversation = editor.read(cx).conversation.clone();
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::{
|
||||
tests::{rust_lang, TestServer},
|
||||
};
|
||||
use call::ActiveCall;
|
||||
use collections::HashMap;
|
||||
use editor::{
|
||||
actions::{
|
||||
ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Redo, Rename, RevertSelectedHunks,
|
||||
@@ -736,60 +735,12 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
|
||||
6..9
|
||||
);
|
||||
rename.editor.update(cx, |rename_editor, cx| {
|
||||
let rename_selection = rename_editor.selections.newest::<usize>(cx);
|
||||
assert_eq!(
|
||||
rename_selection.range(),
|
||||
0..3,
|
||||
"Rename that was triggered from zero selection caret, should propose the whole word."
|
||||
);
|
||||
rename_editor.buffer().update(cx, |rename_buffer, cx| {
|
||||
rename_buffer.edit([(0..3, "THREE")], None, cx);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Cancel the rename, and repeat the same, but use selections instead of cursor movement
|
||||
editor_b.update(cx_b, |editor, cx| {
|
||||
editor.cancel(&editor::actions::Cancel, cx);
|
||||
});
|
||||
let prepare_rename = editor_b.update(cx_b, |editor, cx| {
|
||||
editor.change_selections(None, cx, |s| s.select_ranges([7..8]));
|
||||
editor.rename(&Rename, cx).unwrap()
|
||||
});
|
||||
|
||||
fake_language_server
|
||||
.handle_request::<lsp::request::PrepareRenameRequest, _, _>(|params, _| async move {
|
||||
assert_eq!(params.text_document.uri.as_str(), "file:///dir/one.rs");
|
||||
assert_eq!(params.position, lsp::Position::new(0, 8));
|
||||
Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
|
||||
lsp::Position::new(0, 6),
|
||||
lsp::Position::new(0, 9),
|
||||
))))
|
||||
})
|
||||
.next()
|
||||
.await
|
||||
.unwrap();
|
||||
prepare_rename.await.unwrap();
|
||||
editor_b.update(cx_b, |editor, cx| {
|
||||
use editor::ToOffset;
|
||||
let rename = editor.pending_rename().unwrap();
|
||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||
let lsp_rename_start = rename.range.start.to_offset(&buffer);
|
||||
let lsp_rename_end = rename.range.end.to_offset(&buffer);
|
||||
assert_eq!(lsp_rename_start..lsp_rename_end, 6..9);
|
||||
rename.editor.update(cx, |rename_editor, cx| {
|
||||
let rename_selection = rename_editor.selections.newest::<usize>(cx);
|
||||
assert_eq!(
|
||||
rename_selection.range(),
|
||||
1..2,
|
||||
"Rename that was triggered from a selection, should have the same selection range in the rename proposal"
|
||||
);
|
||||
rename_editor.buffer().update(cx, |rename_buffer, cx| {
|
||||
rename_buffer.edit([(0..lsp_rename_end - lsp_rename_start, "THREE")], None, cx);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let confirm_rename = editor_b.update(cx_b, |editor, cx| {
|
||||
Editor::confirm_rename(editor, &ConfirmRename, cx).unwrap()
|
||||
});
|
||||
@@ -2055,7 +2006,6 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
let inline_blame_off_settings = Some(InlineBlameSettings {
|
||||
enabled: false,
|
||||
delay_ms: None,
|
||||
min_column: None,
|
||||
});
|
||||
cx_a.update(|cx| {
|
||||
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
@@ -2090,7 +2040,15 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
blame_entry("3a3a3a", 2..3),
|
||||
blame_entry("4c4c4c", 3..4),
|
||||
],
|
||||
permalinks: HashMap::default(), // This field is deprecrated
|
||||
permalinks: [
|
||||
("1b1b1b", "http://example.com/codehost/idx-0"),
|
||||
("0d0d0d", "http://example.com/codehost/idx-1"),
|
||||
("3a3a3a", "http://example.com/codehost/idx-2"),
|
||||
("4c4c4c", "http://example.com/codehost/idx-3"),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(sha, url)| (sha.parse().unwrap(), url.parse().unwrap()))
|
||||
.collect(),
|
||||
messages: [
|
||||
("1b1b1b", "message for idx-0"),
|
||||
("0d0d0d", "message for idx-1"),
|
||||
@@ -2100,7 +2058,6 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
.into_iter()
|
||||
.map(|(sha, message)| (sha.parse().unwrap(), message.into()))
|
||||
.collect(),
|
||||
remote_url: Some("git@github.com:zed-industries/zed.git".to_string()),
|
||||
};
|
||||
client_a.fs().set_blame_for_repo(
|
||||
Path::new("/my-repo/.git"),
|
||||
@@ -2169,7 +2126,7 @@ async fn test_git_blame_is_forwarded(cx_a: &mut TestAppContext, cx_b: &mut TestA
|
||||
assert_eq!(details.message, format!("message for idx-{}", idx));
|
||||
assert_eq!(
|
||||
details.permalink.unwrap().to_string(),
|
||||
format!("https://github.com/zed-industries/zed/commit/{}", entry.sha)
|
||||
format!("http://example.com/codehost/idx-{}", idx)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -305,10 +305,6 @@ impl ChannelView {
|
||||
});
|
||||
}
|
||||
ChannelBufferEvent::BufferEdited => {
|
||||
// Emit the edited event on the editor context so that other views can update it's state (e.g. markdown preview)
|
||||
self.editor.update(cx, |_, cx| {
|
||||
cx.emit(EditorEvent::Edited);
|
||||
});
|
||||
if self.editor.read(cx).is_focused(cx) {
|
||||
self.acknowledge_buffer_version(cx);
|
||||
} else {
|
||||
|
||||
@@ -2834,31 +2834,34 @@ fn render_tree_branch(is_last: bool, overdraw: bool, cx: &mut WindowContext) ->
|
||||
let thickness = px(1.);
|
||||
let color = cx.theme().colors().text;
|
||||
|
||||
canvas(move |bounds, cx| {
|
||||
let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
|
||||
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
|
||||
let right = bounds.right();
|
||||
let top = bounds.top();
|
||||
canvas(
|
||||
|_, _| {},
|
||||
move |bounds, _, cx| {
|
||||
let start_x = (bounds.left() + bounds.right() - thickness) / 2.;
|
||||
let start_y = (bounds.top() + bounds.bottom() - thickness) / 2.;
|
||||
let right = bounds.right();
|
||||
let top = bounds.top();
|
||||
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(
|
||||
point(start_x, top),
|
||||
point(
|
||||
start_x + thickness,
|
||||
if is_last {
|
||||
start_y
|
||||
} else {
|
||||
bounds.bottom() + if overdraw { px(1.) } else { px(0.) }
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
color,
|
||||
));
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
));
|
||||
})
|
||||
color,
|
||||
));
|
||||
cx.paint_quad(fill(
|
||||
Bounds::from_corners(point(start_x, start_y), point(right, start_y + thickness)),
|
||||
color,
|
||||
));
|
||||
},
|
||||
)
|
||||
.w(width)
|
||||
.h(line_height)
|
||||
}
|
||||
|
||||
@@ -318,23 +318,26 @@ impl Render for CollabTitlebarItem {
|
||||
}
|
||||
|
||||
fn render_color_ribbon(color: Hsla) -> impl Element {
|
||||
canvas(move |bounds, cx| {
|
||||
let height = bounds.size.height;
|
||||
let horizontal_offset = height;
|
||||
let vertical_offset = px(height.0 / 2.0);
|
||||
let mut path = Path::new(bounds.lower_left());
|
||||
path.curve_to(
|
||||
bounds.origin + point(horizontal_offset, vertical_offset),
|
||||
bounds.origin + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
|
||||
path.curve_to(
|
||||
bounds.lower_right(),
|
||||
bounds.upper_right() + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.lower_left());
|
||||
cx.paint_path(path, color);
|
||||
})
|
||||
canvas(
|
||||
move |_, _| {},
|
||||
move |bounds, _, cx| {
|
||||
let height = bounds.size.height;
|
||||
let horizontal_offset = height;
|
||||
let vertical_offset = px(height.0 / 2.0);
|
||||
let mut path = Path::new(bounds.lower_left());
|
||||
path.curve_to(
|
||||
bounds.origin + point(horizontal_offset, vertical_offset),
|
||||
bounds.origin + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.upper_right() + point(-horizontal_offset, vertical_offset));
|
||||
path.curve_to(
|
||||
bounds.lower_right(),
|
||||
bounds.upper_right() + point(px(0.0), vertical_offset),
|
||||
);
|
||||
path.line_to(bounds.lower_left());
|
||||
cx.paint_path(path, color);
|
||||
},
|
||||
)
|
||||
.h_1()
|
||||
.w_full()
|
||||
}
|
||||
|
||||
@@ -131,10 +131,10 @@ use ui::{
|
||||
use util::{defer, maybe, post_inc, RangeExt, ResultExt, TryFutureExt};
|
||||
use workspace::item::ItemHandle;
|
||||
use workspace::notifications::NotificationId;
|
||||
use workspace::Toast;
|
||||
use workspace::{
|
||||
searchable::SearchEvent, ItemNavHistory, SplitDirection, ViewId, Workspace, WorkspaceId,
|
||||
};
|
||||
use workspace::{OpenInTerminal, OpenTerminal, Toast};
|
||||
|
||||
use crate::hover_links::find_url;
|
||||
|
||||
@@ -476,8 +476,8 @@ pub struct Editor {
|
||||
+ Fn(&mut Self, DisplayPoint, &mut ViewContext<Self>) -> Option<View<ui::ContextMenu>>,
|
||||
>,
|
||||
>,
|
||||
last_layout_bounds: Option<Bounds<Pixels>>,
|
||||
expect_layout_bounds_change: Option<Bounds<Pixels>>,
|
||||
last_bounds: Option<Bounds<Pixels>>,
|
||||
expect_bounds_change: Option<Bounds<Pixels>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -1492,8 +1492,8 @@ impl Editor {
|
||||
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
|
||||
gutter_hovered: false,
|
||||
pixel_position_of_newest_cursor: None,
|
||||
last_layout_bounds: None,
|
||||
expect_layout_bounds_change: None,
|
||||
last_bounds: None,
|
||||
expect_bounds_change: None,
|
||||
gutter_width: Default::default(),
|
||||
style: None,
|
||||
show_cursor_names: false,
|
||||
@@ -1828,29 +1828,6 @@ impl Editor {
|
||||
old_cursor_position: &Anchor,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
// Copy selections to primary selection buffer
|
||||
#[cfg(target_os = "linux")]
|
||||
if local {
|
||||
let selections = self.selections.all::<usize>(cx);
|
||||
let buffer_handle = self.buffer.read(cx).read(cx);
|
||||
|
||||
let mut text = String::new();
|
||||
for (index, selection) in selections.iter().enumerate() {
|
||||
let text_for_selection = buffer_handle
|
||||
.text_for_range(selection.start..selection.end)
|
||||
.collect::<String>();
|
||||
|
||||
text.push_str(&text_for_selection);
|
||||
if index != selections.len() - 1 {
|
||||
text.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
if !text.is_empty() {
|
||||
cx.write_to_primary(ClipboardItem::new(text));
|
||||
}
|
||||
}
|
||||
|
||||
if self.focus_handle.is_focused(cx) && self.leader_peer_id.is_none() {
|
||||
self.buffer.update(cx, |buffer, cx| {
|
||||
buffer.set_active_selections(
|
||||
@@ -4943,25 +4920,6 @@ impl Editor {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_active_item_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
|
||||
if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
|
||||
let project_path = buffer.read(cx).project_path(cx)?;
|
||||
let project = self.project.as_ref()?.read(cx);
|
||||
let entry = project.entry_for_path(&project_path, cx)?;
|
||||
let abs_path = project.absolute_path(&project_path, cx)?;
|
||||
let parent = if entry.is_symlink {
|
||||
abs_path.canonicalize().ok()?
|
||||
} else {
|
||||
abs_path
|
||||
}
|
||||
.parent()?
|
||||
.to_path_buf();
|
||||
Some(parent)
|
||||
}) {
|
||||
cx.dispatch_action(OpenTerminal { working_directory }.boxed_clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_revert_changes(
|
||||
&mut self,
|
||||
selections: &[Selection<Anchor>],
|
||||
@@ -8094,7 +8052,7 @@ impl Editor {
|
||||
.buffer
|
||||
.read(cx)
|
||||
.text_anchor_for_position(selection.head(), cx)?;
|
||||
let (tail_buffer, cursor_buffer_position_end) = self
|
||||
let (tail_buffer, _) = self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.text_anchor_for_position(selection.tail(), cx)?;
|
||||
@@ -8104,7 +8062,6 @@ impl Editor {
|
||||
|
||||
let snapshot = cursor_buffer.read(cx).snapshot();
|
||||
let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
|
||||
let cursor_buffer_offset_end = cursor_buffer_position_end.to_offset(&snapshot);
|
||||
let prepare_rename = project.update(cx, |project, cx| {
|
||||
project.prepare_rename(cursor_buffer.clone(), cursor_buffer_offset, cx)
|
||||
});
|
||||
@@ -8133,8 +8090,6 @@ impl Editor {
|
||||
let rename_buffer_range = rename_range.to_offset(&snapshot);
|
||||
let cursor_offset_in_rename_range =
|
||||
cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
|
||||
let cursor_offset_in_rename_range_end =
|
||||
cursor_buffer_offset_end.saturating_sub(rename_buffer_range.start);
|
||||
|
||||
this.take_rename(false, cx);
|
||||
let buffer = this.buffer.read(cx).read(cx);
|
||||
@@ -8163,23 +8118,7 @@ impl Editor {
|
||||
editor.buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit([(0..0, old_name.clone())], None, cx)
|
||||
});
|
||||
let rename_selection_range = match cursor_offset_in_rename_range
|
||||
.cmp(&cursor_offset_in_rename_range_end)
|
||||
{
|
||||
Ordering::Equal => {
|
||||
editor.select_all(&SelectAll, cx);
|
||||
return editor;
|
||||
}
|
||||
Ordering::Less => {
|
||||
cursor_offset_in_rename_range..cursor_offset_in_rename_range_end
|
||||
}
|
||||
Ordering::Greater => {
|
||||
cursor_offset_in_rename_range_end..cursor_offset_in_rename_range
|
||||
}
|
||||
};
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges([rename_selection_range]);
|
||||
});
|
||||
editor.select_all(&SelectAll, cx);
|
||||
editor
|
||||
});
|
||||
|
||||
@@ -10778,8 +10717,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, _is_valid: bool) -> Ren
|
||||
|
||||
let icon_size = buttons(&diagnostic, cx.block_id)
|
||||
.into_any_element()
|
||||
.layout(AvailableSpace::min_size(), cx)
|
||||
.size;
|
||||
.measure(AvailableSpace::min_size(), cx);
|
||||
|
||||
h_flex()
|
||||
.id(cx.block_id)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@ use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
use git::{
|
||||
blame::{Blame, BlameEntry},
|
||||
permalink::{build_commit_permalink, parse_git_remote_url},
|
||||
Oid,
|
||||
};
|
||||
use gpui::{Model, ModelContext, Subscription, Task};
|
||||
@@ -287,13 +286,11 @@ impl GitBlame {
|
||||
entries,
|
||||
permalinks,
|
||||
messages,
|
||||
remote_url,
|
||||
} = blame.await?;
|
||||
|
||||
let entries = build_blame_entry_sum_tree(entries, snapshot.max_point().row);
|
||||
let commit_details =
|
||||
parse_commit_messages(messages, remote_url, &permalinks, &languages)
|
||||
.await;
|
||||
parse_commit_messages(messages, &permalinks, &languages).await;
|
||||
|
||||
anyhow::Ok((entries, commit_details))
|
||||
}
|
||||
@@ -382,31 +379,13 @@ fn build_blame_entry_sum_tree(entries: Vec<BlameEntry>, max_row: u32) -> SumTree
|
||||
|
||||
async fn parse_commit_messages(
|
||||
messages: impl IntoIterator<Item = (Oid, String)>,
|
||||
remote_url: Option<String>,
|
||||
deprecated_permalinks: &HashMap<Oid, Url>,
|
||||
permalinks: &HashMap<Oid, Url>,
|
||||
languages: &Arc<LanguageRegistry>,
|
||||
) -> HashMap<Oid, CommitDetails> {
|
||||
let mut commit_details = HashMap::default();
|
||||
|
||||
let parsed_remote_url = remote_url.as_deref().and_then(parse_git_remote_url);
|
||||
|
||||
for (oid, message) in messages {
|
||||
let parsed_message = parse_markdown(&message, &languages).await;
|
||||
|
||||
let permalink = if let Some(git_remote) = parsed_remote_url.as_ref() {
|
||||
Some(build_commit_permalink(
|
||||
git::permalink::BuildCommitPermalinkParams {
|
||||
remote: git_remote,
|
||||
sha: oid.to_string().as_str(),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
// DEPRECATED (18 Apr 24): Sending permalinks over the wire is deprecated. Clients
|
||||
// now do the parsing. This is here for backwards compatibility, so that
|
||||
// when an old peer sends a client no `parsed_remote_url` but `deprecated_permalinks`,
|
||||
// we fall back to that.
|
||||
deprecated_permalinks.get(&oid).cloned()
|
||||
};
|
||||
let permalink = permalinks.get(&oid).cloned();
|
||||
|
||||
commit_details.insert(
|
||||
oid,
|
||||
|
||||
@@ -1173,7 +1173,7 @@ impl SearchableItem for Editor {
|
||||
}
|
||||
|
||||
fn search_bar_visibility_changed(&mut self, _visible: bool, _cx: &mut ViewContext<Self>) {
|
||||
self.expect_layout_bounds_change = self.last_layout_bounds;
|
||||
self.expect_bounds_change = self.last_bounds;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::{
|
||||
GoToTypeDefinition, Rename, RevealInFinder, SelectMode, ToggleCodeActions,
|
||||
};
|
||||
use gpui::{DismissEvent, Pixels, Point, Subscription, View, ViewContext};
|
||||
use workspace::OpenInTerminal;
|
||||
|
||||
pub struct MouseContextMenu {
|
||||
pub(crate) position: Point<Pixels>,
|
||||
@@ -84,7 +83,6 @@ pub fn deploy_context_menu(
|
||||
)
|
||||
.separator()
|
||||
.action("Reveal in Finder", Box::new(RevealInFinder))
|
||||
.action("Open in Terminal", Box::new(OpenInTerminal))
|
||||
})
|
||||
};
|
||||
let mouse_context_menu = MouseContextMenu::new(position, context_menu, cx);
|
||||
|
||||
@@ -72,7 +72,7 @@ impl Editor {
|
||||
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let mut scroll_position = self.scroll_manager.scroll_position(&display_map);
|
||||
let original_y = scroll_position.y;
|
||||
if let Some(last_bounds) = self.expect_layout_bounds_change.take() {
|
||||
if let Some(last_bounds) = self.expect_bounds_change.take() {
|
||||
if scroll_position.y != 0. {
|
||||
scroll_position.y += (bounds.top() - last_bounds.top()) / line_height;
|
||||
if scroll_position.y < 0. {
|
||||
|
||||
@@ -936,25 +936,24 @@ impl Render for ExtensionsPage {
|
||||
let view = cx.view().clone();
|
||||
let scroll_handle = self.list.clone();
|
||||
this.child(
|
||||
// canvas(
|
||||
// move |bounds, cx| {
|
||||
// let mut list = uniform_list::<_, ExtensionCard, _>(
|
||||
// view,
|
||||
// "entries",
|
||||
// count,
|
||||
// Self::render_extensions,
|
||||
// )
|
||||
// .size_full()
|
||||
// .pb_4()
|
||||
// .track_scroll(scroll_handle)
|
||||
// .into_any_element();
|
||||
// list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
// list
|
||||
// },
|
||||
// |_bounds, mut list, cx| list.paint(cx),
|
||||
// )
|
||||
// .size_full(),
|
||||
todo!("replace canvas"),
|
||||
canvas(
|
||||
move |bounds, cx| {
|
||||
let mut list = uniform_list::<_, ExtensionCard, _>(
|
||||
view,
|
||||
"entries",
|
||||
count,
|
||||
Self::render_extensions,
|
||||
)
|
||||
.size_full()
|
||||
.pb_4()
|
||||
.track_scroll(scroll_handle)
|
||||
.into_any_element();
|
||||
list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
list
|
||||
},
|
||||
|_bounds, mut list, cx| list.paint(cx),
|
||||
)
|
||||
.size_full(),
|
||||
)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ text.workspace = true
|
||||
time.workspace = true
|
||||
url.workspace = true
|
||||
serde.workspace = true
|
||||
windows.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
unindent.workspace = true
|
||||
|
||||
@@ -14,9 +14,6 @@ use time::OffsetDateTime;
|
||||
use time::UtcOffset;
|
||||
use url::Url;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
pub use git2 as libgit;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
@@ -24,7 +21,6 @@ pub struct Blame {
|
||||
pub entries: Vec<BlameEntry>,
|
||||
pub messages: HashMap<Oid, String>,
|
||||
pub permalinks: HashMap<Oid, Url>,
|
||||
pub remote_url: Option<String>,
|
||||
}
|
||||
|
||||
impl Blame {
|
||||
@@ -45,8 +41,6 @@ impl Blame {
|
||||
|
||||
for entry in entries.iter_mut() {
|
||||
unique_shas.insert(entry.sha);
|
||||
// DEPRECATED (18 Apr 24): Sending permalinks over the wire is deprecated. Clients
|
||||
// now do the parsing.
|
||||
if let Some(remote) = parsed_remote_url.as_ref() {
|
||||
permalinks.entry(entry.sha).or_insert_with(|| {
|
||||
build_commit_permalink(BuildCommitPermalinkParams {
|
||||
@@ -65,23 +59,17 @@ impl Blame {
|
||||
entries,
|
||||
permalinks,
|
||||
messages,
|
||||
remote_url,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const GIT_BLAME_NO_COMMIT_ERROR: &'static str = "fatal: no such ref: HEAD";
|
||||
const GIT_BLAME_NO_PATH: &'static str = "fatal: no such path";
|
||||
|
||||
fn run_git_blame(
|
||||
git_binary: &Path,
|
||||
working_directory: &Path,
|
||||
path: &Path,
|
||||
contents: &Rope,
|
||||
) -> Result<String> {
|
||||
let mut child = Command::new(git_binary);
|
||||
|
||||
child
|
||||
let child = Command::new(git_binary)
|
||||
.current_dir(working_directory)
|
||||
.arg("blame")
|
||||
.arg("--incremental")
|
||||
@@ -90,12 +78,7 @@ fn run_git_blame(
|
||||
.arg(path.as_os_str())
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
#[cfg(windows)]
|
||||
child.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0);
|
||||
|
||||
let child = child
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|e| anyhow!("Failed to start git blame process: {}", e))?;
|
||||
|
||||
@@ -115,10 +98,6 @@ fn run_git_blame(
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let trimmed = stderr.trim();
|
||||
if trimmed == GIT_BLAME_NO_COMMIT_ERROR || trimmed.contains(GIT_BLAME_NO_PATH) {
|
||||
return Ok(String::new());
|
||||
}
|
||||
return Err(anyhow!("git blame process failed: {}", stderr));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,25 +4,15 @@ use collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
pub fn get_messages(working_directory: &Path, shas: &[Oid]) -> Result<HashMap<Oid, String>> {
|
||||
const MARKER: &'static str = "<MARKER>";
|
||||
|
||||
let mut command = Command::new("git");
|
||||
|
||||
command
|
||||
let output = Command::new("git")
|
||||
.current_dir(working_directory)
|
||||
.arg("show")
|
||||
.arg("-s")
|
||||
.arg(format!("--format=%B{}", MARKER))
|
||||
.args(shas.iter().map(ToString::to_string));
|
||||
|
||||
#[cfg(windows)]
|
||||
command.creation_flags(windows::Win32::System::Threading::CREATE_NO_WINDOW.0);
|
||||
|
||||
let output = command
|
||||
.args(shas.iter().map(ToString::to_string))
|
||||
.output()
|
||||
.map_err(|e| anyhow!("Failed to start git blame process: {}", e))?;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::ops::Range;
|
||||
use anyhow::{anyhow, Result};
|
||||
use url::Url;
|
||||
|
||||
pub enum GitHostingProvider {
|
||||
pub(crate) enum GitHostingProvider {
|
||||
Github,
|
||||
Gitlab,
|
||||
Gitee,
|
||||
@@ -90,18 +90,18 @@ pub fn build_permalink(params: BuildPermalinkParams) -> Result<Url> {
|
||||
Ok(permalink)
|
||||
}
|
||||
|
||||
pub struct ParsedGitRemote<'a> {
|
||||
pub(crate) struct ParsedGitRemote<'a> {
|
||||
pub provider: GitHostingProvider,
|
||||
pub owner: &'a str,
|
||||
pub repo: &'a str,
|
||||
}
|
||||
|
||||
pub struct BuildCommitPermalinkParams<'a> {
|
||||
pub(crate) struct BuildCommitPermalinkParams<'a> {
|
||||
pub remote: &'a ParsedGitRemote<'a>,
|
||||
pub sha: &'a str,
|
||||
}
|
||||
|
||||
pub fn build_commit_permalink(params: BuildCommitPermalinkParams) -> Url {
|
||||
pub(crate) fn build_commit_permalink(params: BuildCommitPermalinkParams) -> Url {
|
||||
let BuildCommitPermalinkParams { sha, remote } = params;
|
||||
|
||||
let ParsedGitRemote {
|
||||
@@ -122,7 +122,7 @@ pub fn build_commit_permalink(params: BuildCommitPermalinkParams) -> Url {
|
||||
provider.base_url().join(&path).unwrap()
|
||||
}
|
||||
|
||||
pub fn parse_git_remote_url(url: &str) -> Option<ParsedGitRemote> {
|
||||
pub(crate) fn parse_git_remote_url(url: &str) -> Option<ParsedGitRemote> {
|
||||
if url.starts_with("git@github.com:") || url.starts_with("https://github.com/") {
|
||||
let repo_with_owner = url
|
||||
.trim_start_matches("git@github.com:")
|
||||
|
||||
@@ -115,7 +115,7 @@ wayland-protocols = { version = "0.31.2", features = [
|
||||
] }
|
||||
oo7 = "0.3.0"
|
||||
open = "5.1.2"
|
||||
x11rb = { version = "0.13.0", features = ["allow-unsafe-code", "xkb", "randr", "xinput"] }
|
||||
x11rb = { version = "0.13.0", features = ["allow-unsafe-code", "xkb", "randr"] }
|
||||
xkbcommon = { version = "0.7", features = ["wayland", "x11"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
||||
@@ -547,23 +547,11 @@ impl AppContext {
|
||||
self.platform.window_appearance()
|
||||
}
|
||||
|
||||
/// Writes data to the primary selection buffer.
|
||||
/// Only available on Linux.
|
||||
pub fn write_to_primary(&self, item: ClipboardItem) {
|
||||
self.platform.write_to_primary(item)
|
||||
}
|
||||
|
||||
/// Writes data to the platform clipboard.
|
||||
pub fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
self.platform.write_to_clipboard(item)
|
||||
}
|
||||
|
||||
/// Reads data from the primary selection buffer.
|
||||
/// Only available on Linux.
|
||||
pub fn read_from_primary(&self) -> Option<ClipboardItem> {
|
||||
self.platform.read_from_primary()
|
||||
}
|
||||
|
||||
/// Reads data from the platform clipboard.
|
||||
pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
self.platform.read_from_clipboard()
|
||||
|
||||
@@ -734,8 +734,7 @@ impl VisualTestContext {
|
||||
self.update(|cx| {
|
||||
cx.with_element_context(|cx| {
|
||||
let mut element = f(cx);
|
||||
element.layout(space, cx);
|
||||
cx.with_element_offset(origin, |cx| element.before_paint(cx));
|
||||
element.layout(origin, space, cx);
|
||||
element.paint(cx);
|
||||
});
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
use crate::{
|
||||
util::FluentBuilder, ArenaBox, AvailableSpace, Bounds, DispatchNodeId, ElementContext,
|
||||
ElementId, LayoutId, Pixels, Size, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||
ElementId, LayoutId, Pixels, Point, Size, ViewContext, WindowContext, ELEMENT_ARENA,
|
||||
};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
pub(crate) use smallvec::SmallVec;
|
||||
@@ -45,46 +45,33 @@ use std::{any::Any, fmt::Debug, mem, ops::DerefMut};
|
||||
/// for more details.
|
||||
pub trait Element: 'static + IntoElement {
|
||||
/// The type of state returned from [`Element::before_layout`]. A mutable reference to this state is subsequently
|
||||
/// provided to [`Element::after_layout`], [`Element::before_paint`] and [`Element::paint`].
|
||||
/// provided to [`Element::after_layout`] and [`Element::paint`].
|
||||
type BeforeLayout: 'static;
|
||||
|
||||
/// The type of state returned from [`Element::after_layout`]. A mutable reference to this state is subsequently
|
||||
/// provided to [`Element::before_paint`] and [`Element::paint`].
|
||||
type AfterLayout: 'static;
|
||||
|
||||
/// The type of state returned from [`Element::before_paint`]. A mutable reference to this state is subsequently
|
||||
/// provided to [`Element::paint`].
|
||||
type BeforePaint: 'static;
|
||||
type AfterLayout: 'static;
|
||||
|
||||
/// Before an element can be painted, we need to know where it's going to be and how big it is.
|
||||
/// Use this method to request a layout from Taffy and initialize the element's state.
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout);
|
||||
|
||||
/// todo!()
|
||||
/// After laying out an element, we need to commit its bounds to the current frame for hitbox
|
||||
/// purposes. The state argument is the same state that was returned from [`Element::before_layout()`].
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout);
|
||||
|
||||
/// Before painting an element, we need to commit its bounds to the current frame for hitbox
|
||||
/// purposes.
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Self::BeforePaint;
|
||||
) -> Self::AfterLayout;
|
||||
|
||||
/// Once layout has been completed, this method will be called to paint the element to the screen.
|
||||
/// The state argument is the same state that was returned from [`Element::before_layout()`].
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
);
|
||||
|
||||
@@ -176,7 +163,6 @@ impl<C: RenderOnce> Component<C> {
|
||||
impl<C: RenderOnce> Element for Component<C> {
|
||||
type BeforeLayout = AnyElement;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut element = self
|
||||
@@ -190,23 +176,12 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let bounds = element.after_layout(cx);
|
||||
(bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut AnyElement,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
element.before_paint(cx);
|
||||
element.after_layout(cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -214,7 +189,6 @@ impl<C: RenderOnce> Element for Component<C> {
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
element.paint(cx)
|
||||
@@ -238,59 +212,46 @@ trait ElementObject {
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> LayoutId;
|
||||
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>>;
|
||||
|
||||
fn before_paint(&mut self, cx: &mut ElementContext);
|
||||
fn after_layout(&mut self, cx: &mut ElementContext);
|
||||
|
||||
fn paint(&mut self, cx: &mut ElementContext);
|
||||
|
||||
fn layout(
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> ElementMeasurement;
|
||||
) -> Size<Pixels>;
|
||||
}
|
||||
|
||||
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
|
||||
pub struct Drawable<E: Element> {
|
||||
/// The drawn element.
|
||||
pub element: E,
|
||||
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout, E::BeforePaint>,
|
||||
phase: ElementDrawPhase<E::BeforeLayout, E::AfterLayout>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum ElementDrawPhase<BeforeLayout, AfterLayout, BeforePaint> {
|
||||
enum ElementDrawPhase<BeforeLayout, AfterLayout> {
|
||||
#[default]
|
||||
Start,
|
||||
BeforeLayout {
|
||||
layout_id: LayoutId,
|
||||
before_layout: BeforeLayout,
|
||||
},
|
||||
AfterLayout {
|
||||
LayoutComputed {
|
||||
layout_id: LayoutId,
|
||||
available_space: Option<Size<AvailableSpace>>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
before_layout: BeforeLayout,
|
||||
after_layout: AfterLayout,
|
||||
},
|
||||
BeforePaint {
|
||||
AfterLayout {
|
||||
node_id: DispatchNodeId,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: BeforeLayout,
|
||||
after_layout: AfterLayout,
|
||||
before_paint: BeforePaint,
|
||||
},
|
||||
Painted,
|
||||
}
|
||||
|
||||
/// todo!()
|
||||
#[derive(Default)]
|
||||
pub struct ElementMeasurement {
|
||||
/// The size of the element.
|
||||
pub size: Size<Pixels>,
|
||||
/// The bounds for the focus target inside of the measured element.
|
||||
pub focus_target_bounds: Option<Bounds<Pixels>>,
|
||||
}
|
||||
|
||||
/// A wrapper around an implementer of [`Element`] that allows it to be drawn in a window.
|
||||
impl<E: Element> Drawable<E> {
|
||||
fn new(element: E) -> Self {
|
||||
@@ -314,133 +275,92 @@ impl<E: Element> Drawable<E> {
|
||||
}
|
||||
}
|
||||
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) {
|
||||
match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::BeforeLayout {
|
||||
layout_id,
|
||||
mut before_layout,
|
||||
}
|
||||
| ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
mut before_layout,
|
||||
..
|
||||
} => {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let (focus_target_bounds, after_layout) =
|
||||
self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
let node_id = cx.window.next_frame.dispatch_tree.push_node();
|
||||
let after_layout = self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
self.phase = ElementDrawPhase::AfterLayout {
|
||||
layout_id,
|
||||
available_space: None,
|
||||
node_id,
|
||||
bounds,
|
||||
before_layout,
|
||||
after_layout,
|
||||
};
|
||||
|
||||
focus_target_bounds
|
||||
cx.window.next_frame.dispatch_tree.pop_node();
|
||||
}
|
||||
_ => panic!("must call before_layout before after_layout"),
|
||||
}
|
||||
}
|
||||
|
||||
fn before_paint(&mut self, cx: &mut ElementContext) {
|
||||
match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::AfterLayout {
|
||||
layout_id,
|
||||
mut before_layout,
|
||||
mut after_layout,
|
||||
..
|
||||
} => {
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let node_id = cx.window.next_frame.dispatch_tree.push_node();
|
||||
let before_paint =
|
||||
self.element
|
||||
.before_paint(bounds, &mut before_layout, &mut after_layout, cx);
|
||||
self.phase = ElementDrawPhase::BeforePaint {
|
||||
node_id,
|
||||
bounds,
|
||||
before_layout,
|
||||
after_layout,
|
||||
before_paint,
|
||||
};
|
||||
cx.window.next_frame.dispatch_tree.pop_node();
|
||||
}
|
||||
_ => panic!("must call after_layout before before_paint"),
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(&mut self, cx: &mut ElementContext) -> E::BeforeLayout {
|
||||
match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::BeforePaint {
|
||||
ElementDrawPhase::AfterLayout {
|
||||
node_id,
|
||||
bounds,
|
||||
mut before_layout,
|
||||
mut after_layout,
|
||||
mut before_paint,
|
||||
..
|
||||
} => {
|
||||
cx.window.next_frame.dispatch_tree.set_active_node(node_id);
|
||||
self.element.paint(
|
||||
bounds,
|
||||
&mut before_layout,
|
||||
&mut after_layout,
|
||||
&mut before_paint,
|
||||
cx,
|
||||
);
|
||||
self.element
|
||||
.paint(bounds, &mut before_layout, &mut after_layout, cx);
|
||||
self.phase = ElementDrawPhase::Painted;
|
||||
before_layout
|
||||
}
|
||||
_ => panic!("must call before_paint before paint"),
|
||||
_ => panic!("must call after_layout before paint"),
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> ElementMeasurement {
|
||||
) -> Size<Pixels> {
|
||||
if matches!(&self.phase, ElementDrawPhase::Start) {
|
||||
self.before_layout(cx);
|
||||
}
|
||||
|
||||
match mem::take(&mut self.phase) {
|
||||
let layout_id = match mem::take(&mut self.phase) {
|
||||
ElementDrawPhase::BeforeLayout {
|
||||
layout_id,
|
||||
mut before_layout,
|
||||
before_layout,
|
||||
} => {
|
||||
cx.compute_layout(layout_id, available_space);
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let (focus_target_bounds, after_layout) =
|
||||
self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
self.phase = ElementDrawPhase::AfterLayout {
|
||||
self.phase = ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space: Some(available_space),
|
||||
available_space,
|
||||
before_layout,
|
||||
after_layout,
|
||||
};
|
||||
ElementMeasurement {
|
||||
size: bounds.size,
|
||||
focus_target_bounds,
|
||||
}
|
||||
layout_id
|
||||
}
|
||||
ElementDrawPhase::AfterLayout {
|
||||
ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space: prev_available_space,
|
||||
mut before_layout,
|
||||
..
|
||||
before_layout,
|
||||
} => {
|
||||
if Some(available_space) != prev_available_space {
|
||||
if available_space != prev_available_space {
|
||||
cx.compute_layout(layout_id, available_space);
|
||||
}
|
||||
let bounds = cx.layout_bounds(layout_id);
|
||||
let (focus_target_bounds, after_layout) =
|
||||
self.element.after_layout(bounds, &mut before_layout, cx);
|
||||
self.phase = ElementDrawPhase::AfterLayout {
|
||||
self.phase = ElementDrawPhase::LayoutComputed {
|
||||
layout_id,
|
||||
available_space: Some(available_space),
|
||||
available_space,
|
||||
before_layout,
|
||||
after_layout,
|
||||
};
|
||||
ElementMeasurement {
|
||||
size: bounds.size,
|
||||
focus_target_bounds,
|
||||
}
|
||||
layout_id
|
||||
}
|
||||
_ => panic!("cannot layout after painting"),
|
||||
}
|
||||
_ => panic!("cannot measure after painting"),
|
||||
};
|
||||
|
||||
cx.layout_bounds(layout_id).size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,24 +377,20 @@ where
|
||||
Drawable::before_layout(self, cx)
|
||||
}
|
||||
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
|
||||
Drawable::after_layout(self, cx)
|
||||
}
|
||||
|
||||
fn before_paint(&mut self, cx: &mut ElementContext) {
|
||||
Drawable::before_paint(self, cx);
|
||||
fn after_layout(&mut self, cx: &mut ElementContext) {
|
||||
Drawable::after_layout(self, cx);
|
||||
}
|
||||
|
||||
fn paint(&mut self, cx: &mut ElementContext) {
|
||||
Drawable::paint(self, cx);
|
||||
}
|
||||
|
||||
fn layout(
|
||||
fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> ElementMeasurement {
|
||||
Drawable::layout(self, available_space, cx)
|
||||
) -> Size<Pixels> {
|
||||
Drawable::measure(self, available_space, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,14 +420,9 @@ impl AnyElement {
|
||||
self.0.before_layout(cx)
|
||||
}
|
||||
|
||||
/// todo!()
|
||||
pub fn after_layout(&mut self, cx: &mut ElementContext) -> Option<Bounds<Pixels>> {
|
||||
self.0.after_layout(cx)
|
||||
}
|
||||
|
||||
/// Commits the element bounds of this [AnyElement] for hitbox purposes.
|
||||
pub fn before_paint(&mut self, cx: &mut ElementContext) {
|
||||
self.0.before_paint(cx)
|
||||
pub fn after_layout(&mut self, cx: &mut ElementContext) {
|
||||
self.0.after_layout(cx)
|
||||
}
|
||||
|
||||
/// Paints the element stored in this `AnyElement`.
|
||||
@@ -520,19 +431,30 @@ impl AnyElement {
|
||||
}
|
||||
|
||||
/// Initializes this element and performs layout within the given available space to determine its size.
|
||||
pub fn layout(
|
||||
pub fn measure(
|
||||
&mut self,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> ElementMeasurement {
|
||||
self.0.layout(available_space, cx)
|
||||
) -> Size<Pixels> {
|
||||
self.0.measure(available_space, cx)
|
||||
}
|
||||
|
||||
/// Initializes this element, performs layout if needed and commits its bounds for hitbox purposes.
|
||||
pub fn layout(
|
||||
&mut self,
|
||||
absolute_offset: Point<Pixels>,
|
||||
available_space: Size<AvailableSpace>,
|
||||
cx: &mut ElementContext,
|
||||
) -> Size<Pixels> {
|
||||
let size = self.measure(available_space, cx);
|
||||
cx.with_absolute_element_offset(absolute_offset, |cx| self.after_layout(cx));
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for AnyElement {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let layout_id = self.before_layout(cx);
|
||||
@@ -544,19 +466,8 @@ impl Element for AnyElement {
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let bounds = self.after_layout(cx);
|
||||
(bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.before_paint(cx)
|
||||
self.after_layout(cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -564,7 +475,6 @@ impl Element for AnyElement {
|
||||
_: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.paint(cx)
|
||||
@@ -597,7 +507,6 @@ impl IntoElement for Empty {
|
||||
impl Element for Empty {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
(cx.request_layout(&crate::Style::default(), None), ())
|
||||
@@ -606,17 +515,7 @@ impl Element for Empty {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_state: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -626,7 +525,6 @@ impl Element for Empty {
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ impl ParentElement for Anchored {
|
||||
impl Element for Anchored {
|
||||
type BeforeLayout = AnchoredState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
|
||||
let child_layout_ids = self
|
||||
@@ -92,27 +91,9 @@ impl Element for Anchored {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let mut focus_target_bounds = None;
|
||||
for child in &mut self.children {
|
||||
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
|
||||
if focus_target_bounds.is_none() {
|
||||
focus_target_bounds = Some(child_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
(focus_target_bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
if before_layout.child_layout_ids.is_empty() {
|
||||
@@ -186,7 +167,7 @@ impl Element for Anchored {
|
||||
|
||||
cx.with_element_offset(offset, |cx| {
|
||||
for child in &mut self.children {
|
||||
child.before_paint(cx);
|
||||
child.after_layout(cx);
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -196,7 +177,6 @@ impl Element for Anchored {
|
||||
_bounds: crate::Bounds<crate::Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
for child in &mut self.children {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::{AnyElement, Bounds, Element, ElementId, IntoElement, Pixels};
|
||||
use crate::{AnyElement, Element, ElementId, IntoElement};
|
||||
|
||||
pub use easing::*;
|
||||
|
||||
@@ -86,8 +86,8 @@ struct AnimationState {
|
||||
|
||||
impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||
type BeforeLayout = AnyElement;
|
||||
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(
|
||||
&mut self,
|
||||
@@ -136,29 +136,18 @@ impl<E: IntoElement + 'static> Element for AnimationElement<E> {
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_bounds: crate::Bounds<crate::Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut crate::ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, ()) {
|
||||
(element.after_layout(cx), ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut crate::ElementContext,
|
||||
) -> Self::BeforePaint {
|
||||
element.before_paint(cx);
|
||||
) -> Self::AfterLayout {
|
||||
element.after_layout(cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_bounds: crate::Bounds<crate::Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut crate::ElementContext,
|
||||
) {
|
||||
element.paint(cx);
|
||||
|
||||
@@ -4,8 +4,12 @@ use crate::{Bounds, Element, ElementContext, IntoElement, Pixels, Style, StyleRe
|
||||
|
||||
/// Construct a canvas element with the given paint callback.
|
||||
/// Useful for adding short term custom drawing to a view.
|
||||
pub fn canvas(paint: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext)) -> Canvas {
|
||||
pub fn canvas<T>(
|
||||
after_layout: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext) -> T,
|
||||
paint: impl 'static + FnOnce(Bounds<Pixels>, T, &mut ElementContext),
|
||||
) -> Canvas<T> {
|
||||
Canvas {
|
||||
after_layout: Some(Box::new(after_layout)),
|
||||
paint: Some(Box::new(paint)),
|
||||
style: StyleRefinement::default(),
|
||||
}
|
||||
@@ -13,12 +17,13 @@ pub fn canvas(paint: impl 'static + FnOnce(Bounds<Pixels>, &mut ElementContext))
|
||||
|
||||
/// A canvas element, meant for accessing the low level paint API without defining a whole
|
||||
/// custom element
|
||||
pub struct Canvas {
|
||||
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext)>>,
|
||||
pub struct Canvas<T> {
|
||||
after_layout: Option<Box<dyn FnOnce(Bounds<Pixels>, &mut ElementContext) -> T>>,
|
||||
paint: Option<Box<dyn FnOnce(Bounds<Pixels>, T, &mut ElementContext)>>,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
impl IntoElement for Canvas {
|
||||
impl<T: 'static> IntoElement for Canvas<T> {
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
@@ -26,10 +31,9 @@ impl IntoElement for Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
impl Element for Canvas {
|
||||
impl<T: 'static> Element for Canvas<T> {
|
||||
type BeforeLayout = Style;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
type AfterLayout = Option<T>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (crate::LayoutId, Self::BeforeLayout) {
|
||||
let mut style = Style::default();
|
||||
@@ -40,35 +44,28 @@ impl Element for Canvas {
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Style,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<T> {
|
||||
Some(self.after_layout.take().unwrap()(bounds, cx))
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
style: &mut Style,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
style.paint(bounds, cx, |cx| (self.paint.take().unwrap())(bounds, cx));
|
||||
let after_layout = after_layout.take().unwrap();
|
||||
style.paint(bounds, cx, |cx| {
|
||||
(self.paint.take().unwrap())(bounds, after_layout, cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for Canvas {
|
||||
impl<T> Styled for Canvas<T> {
|
||||
fn style(&mut self) -> &mut crate::StyleRefinement {
|
||||
&mut self.style
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ impl Deferred {
|
||||
impl Element for Deferred {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, ()) {
|
||||
let layout_id = self.child.as_mut().unwrap().before_layout(cx);
|
||||
@@ -40,17 +39,6 @@ impl Element for Deferred {
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
let bounds = self.child.as_mut().unwrap().after_layout(cx);
|
||||
(bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let child = self.child.take().unwrap();
|
||||
let element_offset = cx.element_offset();
|
||||
@@ -62,7 +50,6 @@ impl Element for Deferred {
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_before_paint: &mut Self::BeforePaint,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -1121,8 +1121,7 @@ impl ParentElement for Div {
|
||||
|
||||
impl Element for Div {
|
||||
type BeforeLayout = DivFrameState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut child_layout_ids = SmallVec::new();
|
||||
@@ -1144,7 +1143,7 @@ impl Element for Div {
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
) -> Option<Hitbox> {
|
||||
let mut child_min = point(Pixels::MAX, Pixels::MAX);
|
||||
let mut child_max = Point::default();
|
||||
let content_size = if before_layout.child_layout_ids.is_empty() {
|
||||
@@ -1178,49 +1177,25 @@ impl Element for Div {
|
||||
(child_max - child_min).into()
|
||||
};
|
||||
|
||||
let focus_target_bounds = self.interactivity.after_layout(
|
||||
self.interactivity.after_layout(
|
||||
bounds,
|
||||
content_size,
|
||||
cx,
|
||||
|_, scroll_offset, mut focus_target_bounds, cx| {
|
||||
|_style, scroll_offset, hitbox, cx| {
|
||||
cx.with_element_offset(scroll_offset, |cx| {
|
||||
for child in &mut self.children {
|
||||
if let Some(child_focus_bounds) = child.after_layout(cx) {
|
||||
focus_target_bounds = Some(child_focus_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
focus_target_bounds
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
(focus_target_bounds, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.before_paint(bounds, cx, |_style, scroll_offset, hitbox, cx| {
|
||||
cx.with_element_offset(scroll_offset, |cx| {
|
||||
for child in &mut self.children {
|
||||
child.before_paint(cx);
|
||||
child.after_layout(cx);
|
||||
}
|
||||
});
|
||||
hitbox
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Option<Hitbox>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -1361,13 +1336,13 @@ impl Interactivity {
|
||||
)
|
||||
}
|
||||
|
||||
/// todo!()
|
||||
/// Commit the bounds of this element according to this interactivity state's configured styles.
|
||||
pub fn after_layout<R>(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
content_size: Size<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Bounds<Pixels>>, &mut ElementContext) -> R,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
|
||||
) -> R {
|
||||
self.content_size = content_size;
|
||||
cx.with_element_state::<InteractiveElementState, _>(
|
||||
@@ -1392,40 +1367,6 @@ impl Interactivity {
|
||||
}
|
||||
}
|
||||
|
||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
|
||||
let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
|
||||
let focus_target_bounds =
|
||||
self.tracked_focus_handle.as_ref().and_then(|focus_handle| {
|
||||
if focus_handle.is_focused(cx) {
|
||||
Some(bounds)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
let result = f(&style, scroll_offset, focus_target_bounds, cx);
|
||||
(result, element_state)
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Commit the bounds of this element according to this interactivity state's configured styles.
|
||||
pub fn before_paint<R>(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
f: impl FnOnce(&Style, Point<Pixels>, Option<Hitbox>, &mut ElementContext) -> R,
|
||||
) -> R {
|
||||
cx.with_element_state::<InteractiveElementState, _>(
|
||||
self.element_id.clone(),
|
||||
|element_state, cx| {
|
||||
let mut element_state =
|
||||
element_state.map(|element_state| element_state.unwrap_or_default());
|
||||
let style = self.compute_style_internal(None, element_state.as_mut(), cx);
|
||||
|
||||
cx.with_text_style(style.text_style().cloned(), |cx| {
|
||||
cx.with_content_mask(style.overflow_mask(bounds, cx.rem_size()), |cx| {
|
||||
let hitbox = if self.should_insert_hitbox(&style) {
|
||||
@@ -1434,11 +1375,7 @@ impl Interactivity {
|
||||
None
|
||||
};
|
||||
|
||||
let scroll_offset = self
|
||||
.scroll_offset
|
||||
.as_ref()
|
||||
.map(|scroll_offset| *scroll_offset.borrow())
|
||||
.unwrap_or_default();
|
||||
let scroll_offset = self.clamp_scroll_position(bounds, &style, cx);
|
||||
let result = f(&style, scroll_offset, hitbox, cx);
|
||||
(result, element_state)
|
||||
})
|
||||
@@ -2326,7 +2263,6 @@ where
|
||||
{
|
||||
type BeforeLayout = E::BeforeLayout;
|
||||
type AfterLayout = E::AfterLayout;
|
||||
type BeforePaint = E::BeforePaint;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.element.before_layout(cx)
|
||||
@@ -2335,21 +2271,10 @@ where
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
state: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.element.after_layout(bounds, before_layout, cx)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> E::BeforePaint {
|
||||
self.element
|
||||
.before_paint(bounds, before_layout, after_layout, cx)
|
||||
) -> E::AfterLayout {
|
||||
self.element.after_layout(bounds, state, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -2357,11 +2282,9 @@ where
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.element
|
||||
.paint(bounds, before_layout, after_layout, before_paint, cx)
|
||||
self.element.paint(bounds, before_layout, after_layout, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2423,7 +2346,6 @@ where
|
||||
{
|
||||
type BeforeLayout = E::BeforeLayout;
|
||||
type AfterLayout = E::AfterLayout;
|
||||
type BeforePaint = E::BeforePaint;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.element.before_layout(cx)
|
||||
@@ -2432,21 +2354,10 @@ where
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
state: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.element.after_layout(bounds, before_layout, cx)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> E::BeforePaint {
|
||||
self.element
|
||||
.before_paint(bounds, before_layout, after_layout, cx)
|
||||
) -> E::AfterLayout {
|
||||
self.element.after_layout(bounds, state, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
@@ -2454,11 +2365,9 @@ where
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.element
|
||||
.paint(bounds, before_layout, after_layout, before_paint, cx);
|
||||
self.element.paint(bounds, before_layout, after_layout, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -230,8 +230,7 @@ impl Img {
|
||||
|
||||
impl Element for Img {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let layout_id = self.interactivity.before_layout(cx, |mut style, cx| {
|
||||
@@ -262,29 +261,16 @@ impl Element for Img {
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.interactivity
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, _, _| {});
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.before_paint(bounds, cx, |_, _, hitbox, _| hitbox)
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Self::BeforePaint,
|
||||
_: &mut Self::BeforeLayout,
|
||||
hitbox: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let source = self.source.clone();
|
||||
|
||||
@@ -89,28 +89,17 @@ pub enum ListSizingBehavior {
|
||||
Auto,
|
||||
}
|
||||
|
||||
/// Layout information computed by the [List] element during `after_layout`.
|
||||
pub struct ListLayout {
|
||||
struct LayoutItemsResponse {
|
||||
max_item_width: Pixels,
|
||||
scroll_top: ListOffset,
|
||||
items: VecDeque<MeasuredItem>,
|
||||
focus_target: Option<FocusTarget>,
|
||||
available_item_space: Size<AvailableSpace>,
|
||||
item_elements: VecDeque<AnyElement>,
|
||||
}
|
||||
|
||||
struct MeasuredItem {
|
||||
element: AnyElement,
|
||||
size: Size<Pixels>,
|
||||
}
|
||||
|
||||
struct FocusTarget {
|
||||
item_index: usize,
|
||||
bounds_in_item: Bounds<Pixels>,
|
||||
}
|
||||
|
||||
/// Frame state used by the [List] element during paint.
|
||||
pub struct ListBeforePaintState {
|
||||
/// Frame state used by the [List] element after layout.
|
||||
pub struct ListAfterLayoutState {
|
||||
hitbox: Hitbox,
|
||||
layout: ListLayout,
|
||||
layout: LayoutItemsResponse,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -387,14 +376,13 @@ impl StateInner {
|
||||
available_height: Pixels,
|
||||
padding: &Edges<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
) -> ListLayout {
|
||||
) -> LayoutItemsResponse {
|
||||
let old_items = self.items.clone();
|
||||
let mut measured_items = VecDeque::new();
|
||||
let mut items = VecDeque::new();
|
||||
let mut item_elements = VecDeque::new();
|
||||
let mut rendered_height = padding.top;
|
||||
let mut max_item_width = px(0.);
|
||||
let mut scroll_top = self.logical_scroll_top();
|
||||
let mut focus_target = None;
|
||||
|
||||
let available_item_space = size(
|
||||
available_width.map_or(AvailableSpace::MinContent, |width| {
|
||||
@@ -423,20 +411,10 @@ impl StateInner {
|
||||
// If we're within the visible area or the height wasn't cached, render and measure the item's element
|
||||
if visible_height < available_height || size.is_none() {
|
||||
let mut element = (self.render_item)(scroll_top.item_ix + ix, cx);
|
||||
let element_measurement = element.layout(available_item_space, cx);
|
||||
size = Some(element_measurement.size);
|
||||
let element_size = element.measure(available_item_space, cx);
|
||||
size = Some(element_size);
|
||||
if visible_height < available_height {
|
||||
if let Some(focus_target_bounds) = element_measurement.focus_target_bounds {
|
||||
focus_target = Some(FocusTarget {
|
||||
item_index: items.len(),
|
||||
bounds_in_item: focus_target_bounds,
|
||||
});
|
||||
}
|
||||
|
||||
items.push_back(MeasuredItem {
|
||||
element,
|
||||
size: element_measurement.size,
|
||||
});
|
||||
item_elements.push_back(element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,26 +435,11 @@ impl StateInner {
|
||||
cursor.prev(&());
|
||||
if cursor.item().is_some() {
|
||||
let mut element = (self.render_item)(cursor.start().0, cx);
|
||||
let element_measurement = element.layout(available_item_space, cx);
|
||||
let element_size = element.measure(available_item_space, cx);
|
||||
|
||||
rendered_height += element_measurement.size.height;
|
||||
measured_items.push_front(ListItem::Rendered {
|
||||
size: element_measurement.size,
|
||||
});
|
||||
|
||||
items.push_front(MeasuredItem {
|
||||
element,
|
||||
size: element_measurement.size,
|
||||
});
|
||||
|
||||
if let Some(focus_target_bounds) = element_measurement.focus_target_bounds {
|
||||
focus_target = Some(FocusTarget {
|
||||
item_index: 0,
|
||||
bounds_in_item: focus_target_bounds,
|
||||
});
|
||||
} else if let Some(focus_target) = focus_target.as_mut() {
|
||||
focus_target.item_index += 1;
|
||||
}
|
||||
rendered_height += element_size.height;
|
||||
measured_items.push_front(ListItem::Rendered { size: element_size });
|
||||
item_elements.push_front(element)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -511,7 +474,7 @@ impl StateInner {
|
||||
*size
|
||||
} else {
|
||||
let mut element = (self.render_item)(cursor.start().0, cx);
|
||||
element.layout(available_item_space, cx).size
|
||||
element.measure(available_item_space, cx)
|
||||
};
|
||||
|
||||
leading_overdraw += size.height;
|
||||
@@ -530,11 +493,11 @@ impl StateInner {
|
||||
|
||||
self.items = new_items;
|
||||
|
||||
ListLayout {
|
||||
LayoutItemsResponse {
|
||||
max_item_width,
|
||||
scroll_top,
|
||||
items,
|
||||
focus_target,
|
||||
available_item_space,
|
||||
item_elements,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -560,8 +523,7 @@ pub struct ListOffset {
|
||||
|
||||
impl Element for List {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = Option<ListLayout>;
|
||||
type BeforePaint = ListBeforePaintState;
|
||||
type AfterLayout = ListAfterLayoutState;
|
||||
|
||||
fn before_layout(
|
||||
&mut self,
|
||||
@@ -630,15 +592,17 @@ impl Element for List {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
) -> ListAfterLayoutState {
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
state.reset = false;
|
||||
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
|
||||
// If the width of the list has changed, invalidate all cached item heights
|
||||
if state.last_layout_bounds.map_or(true, |last_bounds| {
|
||||
last_bounds.size.width != bounds.size.width
|
||||
@@ -650,77 +614,48 @@ impl Element for List {
|
||||
}
|
||||
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
let layout = state.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
|
||||
let focus_target_bounds = layout.focus_target.as_ref().map(|focus_target| {
|
||||
let mut item_origin =
|
||||
bounds.origin + Point::new(px(0.), padding.top - layout.scroll_top.offset_in_item);
|
||||
item_origin.y -= layout.scroll_top.offset_in_item;
|
||||
for item in layout.items.iter().take(focus_target.item_index) {
|
||||
item_origin.y += item.size.height;
|
||||
}
|
||||
Bounds::new(
|
||||
item_origin + focus_target.bounds_in_item.origin,
|
||||
focus_target.bounds_in_item.size,
|
||||
)
|
||||
});
|
||||
(focus_target_bounds, Some(layout))
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> ListBeforePaintState {
|
||||
let mut layout = layout.take().unwrap();
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
state.reset = false;
|
||||
|
||||
let mut style = Style::default();
|
||||
style.refine(&self.style);
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
let mut layout_response =
|
||||
state.layout_items(Some(bounds.size.width), bounds.size.height, &padding, cx);
|
||||
|
||||
// Only paint the visible items, if there is actually any space for them (taking padding into account)
|
||||
if bounds.size.height > padding.top + padding.bottom {
|
||||
// Paint the visible items
|
||||
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||
let mut item_origin = bounds.origin + Point::new(px(0.), padding.top);
|
||||
item_origin.y -= layout.scroll_top.offset_in_item;
|
||||
for item in &mut layout.items {
|
||||
cx.with_absolute_element_offset(item_origin, |cx| {
|
||||
item.element.before_paint(cx)
|
||||
});
|
||||
item_origin.y += item.size.height;
|
||||
item_origin.y -= layout_response.scroll_top.offset_in_item;
|
||||
for mut item_element in &mut layout_response.item_elements {
|
||||
let item_size = item_element.measure(layout_response.available_item_space, cx);
|
||||
item_element.layout(item_origin, layout_response.available_item_space, cx);
|
||||
item_origin.y += item_size.height;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
state.last_layout_bounds = Some(bounds);
|
||||
state.last_padding = Some(padding);
|
||||
ListBeforePaintState { hitbox, layout }
|
||||
ListAfterLayoutState {
|
||||
hitbox,
|
||||
layout: layout_response,
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
before_paint: &mut Self::BeforePaint,
|
||||
after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut crate::ElementContext,
|
||||
) {
|
||||
cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
|
||||
for item in &mut before_paint.layout.items {
|
||||
item.element.paint(cx);
|
||||
for item in &mut after_layout.layout.item_elements {
|
||||
item.paint(cx);
|
||||
}
|
||||
});
|
||||
|
||||
let list_state = self.state.clone();
|
||||
let height = bounds.size.height;
|
||||
let scroll_top = before_paint.layout.scroll_top;
|
||||
let hitbox_id = before_paint.hitbox.id;
|
||||
let scroll_top = after_layout.layout.scroll_top;
|
||||
let hitbox_id = after_layout.hitbox.id;
|
||||
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble && hitbox_id.is_hovered(cx) {
|
||||
list_state.0.borrow_mut().scroll(
|
||||
|
||||
@@ -38,8 +38,7 @@ impl Svg {
|
||||
|
||||
impl Element for Svg {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let layout_id = self
|
||||
@@ -53,28 +52,15 @@ impl Element for Svg {
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
self.interactivity
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, _, _| {});
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.before_paint(bounds, cx, |_, _, hitbox, _| hitbox)
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, _| hitbox)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Option<Hitbox>,
|
||||
cx: &mut ElementContext,
|
||||
) where
|
||||
|
||||
@@ -19,7 +19,6 @@ use util::ResultExt;
|
||||
impl Element for &'static str {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut state = TextState::default();
|
||||
@@ -28,19 +27,9 @@ impl Element for &'static str {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_text_state: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -49,7 +38,6 @@ impl Element for &'static str {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut TextState,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut (),
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -76,7 +64,6 @@ impl IntoElement for String {
|
||||
impl Element for SharedString {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut state = TextState::default();
|
||||
@@ -85,19 +72,9 @@ impl Element for SharedString {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_text_state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -106,8 +83,7 @@ impl Element for SharedString {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
let text_str: &str = self.as_ref();
|
||||
@@ -174,7 +150,6 @@ impl StyledText {
|
||||
impl Element for StyledText {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut state = TextState::default();
|
||||
@@ -183,19 +158,9 @@ impl Element for StyledText {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_state: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) {
|
||||
}
|
||||
@@ -205,7 +170,6 @@ impl Element for StyledText {
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut (),
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
text_state.paint(bounds, &self.text, cx)
|
||||
@@ -439,27 +403,16 @@ impl InteractiveText {
|
||||
|
||||
impl Element for InteractiveText {
|
||||
type BeforeLayout = TextState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Hitbox;
|
||||
type AfterLayout = Hitbox;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.text.before_layout(cx)
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(None, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Hitbox {
|
||||
cx.with_element_state::<InteractiveTextState, _>(
|
||||
@@ -477,7 +430,7 @@ impl Element for InteractiveText {
|
||||
}
|
||||
}
|
||||
|
||||
self.text.before_paint(bounds, state, &mut (), cx);
|
||||
self.text.after_layout(bounds, state, cx);
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
(hitbox, interactive_state)
|
||||
},
|
||||
@@ -488,7 +441,6 @@ impl Element for InteractiveText {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
text_state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Hitbox,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -625,7 +577,7 @@ impl Element for InteractiveText {
|
||||
});
|
||||
}
|
||||
|
||||
self.text.paint(bounds, text_state, &mut (), &mut (), cx);
|
||||
self.text.paint(bounds, text_state, &mut (), cx);
|
||||
|
||||
((), Some(interactive_state))
|
||||
},
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
use crate::{
|
||||
point, px, size, AnyElement, AvailableSpace, Bounds, ContentMask, Element, ElementContext,
|
||||
ElementId, ElementMeasurement, Hitbox, InteractiveElement, Interactivity, IntoElement,
|
||||
LayoutId, Pixels, Render, ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext,
|
||||
WindowContext,
|
||||
ElementId, Hitbox, InteractiveElement, Interactivity, IntoElement, LayoutId, Pixels, Render,
|
||||
ScrollHandle, Size, StyleRefinement, Styled, View, ViewContext, WindowContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
@@ -71,9 +70,8 @@ pub struct UniformList {
|
||||
|
||||
/// Frame state used by the [UniformList].
|
||||
pub struct UniformListFrameState {
|
||||
item_height: Pixels,
|
||||
item_size: Size<Pixels>,
|
||||
items: SmallVec<[AnyElement; 32]>,
|
||||
visible_range: Range<usize>,
|
||||
}
|
||||
|
||||
/// A handle for controlling the scroll position of a uniform list.
|
||||
@@ -107,22 +105,19 @@ impl Styled for UniformList {
|
||||
|
||||
impl Element for UniformList {
|
||||
type BeforeLayout = UniformListFrameState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<Hitbox>;
|
||||
type AfterLayout = Option<Hitbox>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let max_items = self.item_count;
|
||||
let item_measurement = self.measure_item(None, cx);
|
||||
let item_size = self.measure_item(None, cx);
|
||||
let layout_id = self.interactivity.before_layout(cx, |style, cx| {
|
||||
cx.request_measured_layout(style, move |known_dimensions, available_space, _cx| {
|
||||
let desired_height = item_measurement.size.height * max_items;
|
||||
let desired_height = item_size.height * max_items;
|
||||
let width = known_dimensions
|
||||
.width
|
||||
.unwrap_or(match available_space.width {
|
||||
AvailableSpace::Definite(x) => x,
|
||||
AvailableSpace::MinContent | AvailableSpace::MaxContent => {
|
||||
item_measurement.size.width
|
||||
}
|
||||
AvailableSpace::MinContent | AvailableSpace::MaxContent => item_size.width,
|
||||
});
|
||||
|
||||
let height = match available_space.height {
|
||||
@@ -136,9 +131,8 @@ impl Element for UniformList {
|
||||
(
|
||||
layout_id,
|
||||
UniformListFrameState {
|
||||
item_height: Pixels::default(),
|
||||
item_size,
|
||||
items: SmallVec::new(),
|
||||
visible_range: 0..0,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -146,26 +140,28 @@ impl Element for UniformList {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::BeforeLayout,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
) -> Option<Hitbox> {
|
||||
let style = self.interactivity.compute_style(None, cx);
|
||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
|
||||
let padded_size = size(
|
||||
bounds.size.width - border.left - padding.left - border.right - padding.right,
|
||||
bounds.size.height - border.top - padding.top - border.bottom - padding.bottom,
|
||||
let padded_bounds = Bounds::from_corners(
|
||||
bounds.origin + point(border.left + padding.left, border.top + padding.top),
|
||||
bounds.lower_right()
|
||||
- point(border.right + padding.right, border.bottom + padding.bottom),
|
||||
);
|
||||
let item_height = self.measure_item(Some(padded_size.width), cx).size.height;
|
||||
|
||||
let content_size = Size {
|
||||
width: padded_size.width,
|
||||
height: item_height * self.item_count + padding.top + padding.bottom,
|
||||
width: padded_bounds.size.width,
|
||||
height: before_layout.item_size.height * self.item_count + padding.top + padding.bottom,
|
||||
};
|
||||
|
||||
let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
|
||||
// todo!("add support for scrolling to the focused element?");
|
||||
let deferred_scroll_to_item = self
|
||||
|
||||
let item_height = self.measure_item(Some(padded_bounds.size.width), cx).height;
|
||||
let shared_scroll_to_item = self
|
||||
.scroll_handle
|
||||
.as_mut()
|
||||
.and_then(|handle| handle.deferred_scroll_to_item.take());
|
||||
@@ -174,103 +170,73 @@ impl Element for UniformList {
|
||||
bounds,
|
||||
content_size,
|
||||
cx,
|
||||
|style, mut scroll_offset, mut focus_target_bounds, cx| {
|
||||
if self.item_count == 0 {
|
||||
return (focus_target_bounds, ());
|
||||
}
|
||||
|
||||
|style, mut scroll_offset, hitbox, cx| {
|
||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
|
||||
let padded_size = size(
|
||||
bounds.size.width - border.left - padding.left - border.right - padding.right,
|
||||
bounds.size.height - border.top - padding.top - border.bottom - padding.bottom,
|
||||
);
|
||||
|
||||
let content_height = item_height * self.item_count + padding.top + padding.bottom;
|
||||
let min_scroll_offset = padded_size.height - content_height;
|
||||
let is_scrolled = scroll_offset.y != px(0.);
|
||||
|
||||
if is_scrolled && scroll_offset.y < min_scroll_offset {
|
||||
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
|
||||
scroll_offset.y = min_scroll_offset;
|
||||
}
|
||||
|
||||
if let Some(ix) = deferred_scroll_to_item {
|
||||
let list_height = padded_size.height;
|
||||
let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
|
||||
let item_top = item_height * ix + padding.top;
|
||||
let item_bottom = item_top + item_height;
|
||||
let scroll_top = -updated_scroll_offset.y;
|
||||
if item_top < scroll_top + padding.top {
|
||||
updated_scroll_offset.y = -(item_top) + padding.top;
|
||||
} else if item_bottom > scroll_top + list_height - padding.bottom {
|
||||
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
|
||||
}
|
||||
scroll_offset = *updated_scroll_offset;
|
||||
}
|
||||
|
||||
let first_visible_element_ix =
|
||||
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
|
||||
let last_visible_element_ix =
|
||||
((-scroll_offset.y + padded_size.height) / item_height).ceil() as usize;
|
||||
let visible_range =
|
||||
first_visible_element_ix..cmp::min(last_visible_element_ix, self.item_count);
|
||||
|
||||
let mut items = (self.render_items)(visible_range.clone(), cx);
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(padded_size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
for mut item in items {
|
||||
let measurement = item.layout(available_space, cx);
|
||||
if measurement.focus_target_bounds.is_some() {
|
||||
focus_target_bounds = measurement.focus_target_bounds;
|
||||
}
|
||||
state.items.push(item);
|
||||
}
|
||||
state.item_height = item_height;
|
||||
state.visible_range = visible_range;
|
||||
|
||||
(focus_target_bounds, ())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
state: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<Hitbox> {
|
||||
self.interactivity
|
||||
.before_paint(bounds, cx, |style, scroll_offset, hitbox, cx| {
|
||||
let border = style.border_widths.to_pixels(cx.rem_size());
|
||||
let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
|
||||
let padded_bounds = Bounds::from_corners(
|
||||
bounds.origin + point(border.left + padding.left, border.top),
|
||||
bounds.lower_right() - point(border.right + padding.right, border.bottom),
|
||||
);
|
||||
|
||||
let content_mask = ContentMask { bounds };
|
||||
cx.with_content_mask(Some(content_mask), |cx| {
|
||||
for (item, ix) in state.items.iter_mut().zip(state.visible_range.clone()) {
|
||||
let item_y = state.item_height * ix + scroll_offset.y + padding.top;
|
||||
let item_origin = padded_bounds.origin + point(px(0.), item_y);
|
||||
cx.with_absolute_element_offset(item_origin, |cx| item.before_paint(cx));
|
||||
if self.item_count > 0 {
|
||||
let content_height =
|
||||
item_height * self.item_count + padding.top + padding.bottom;
|
||||
let min_scroll_offset = padded_bounds.size.height - content_height;
|
||||
let is_scrolled = scroll_offset.y != px(0.);
|
||||
|
||||
if is_scrolled && scroll_offset.y < min_scroll_offset {
|
||||
shared_scroll_offset.borrow_mut().y = min_scroll_offset;
|
||||
scroll_offset.y = min_scroll_offset;
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(ix) = shared_scroll_to_item {
|
||||
let list_height = padded_bounds.size.height;
|
||||
let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
|
||||
let item_top = item_height * ix + padding.top;
|
||||
let item_bottom = item_top + item_height;
|
||||
let scroll_top = -updated_scroll_offset.y;
|
||||
if item_top < scroll_top + padding.top {
|
||||
updated_scroll_offset.y = -(item_top) + padding.top;
|
||||
} else if item_bottom > scroll_top + list_height - padding.bottom {
|
||||
updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
|
||||
}
|
||||
scroll_offset = *updated_scroll_offset;
|
||||
}
|
||||
|
||||
let first_visible_element_ix =
|
||||
(-(scroll_offset.y + padding.top) / item_height).floor() as usize;
|
||||
let last_visible_element_ix = ((-scroll_offset.y + padded_bounds.size.height)
|
||||
/ item_height)
|
||||
.ceil() as usize;
|
||||
let visible_range = first_visible_element_ix
|
||||
..cmp::min(last_visible_element_ix, self.item_count);
|
||||
|
||||
let mut items = (self.render_items)(visible_range.clone(), cx);
|
||||
let content_mask = ContentMask { bounds };
|
||||
cx.with_content_mask(Some(content_mask), |cx| {
|
||||
for (mut item, ix) in items.into_iter().zip(visible_range) {
|
||||
let item_origin = padded_bounds.origin
|
||||
+ point(px(0.), item_height * ix + scroll_offset.y + padding.top);
|
||||
let available_space = size(
|
||||
AvailableSpace::Definite(padded_bounds.size.width),
|
||||
AvailableSpace::Definite(item_height),
|
||||
);
|
||||
item.layout(item_origin, available_space, cx);
|
||||
before_layout.items.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hitbox
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
bounds: Bounds<crate::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Option<Hitbox>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
@@ -298,13 +264,9 @@ impl UniformList {
|
||||
self
|
||||
}
|
||||
|
||||
fn measure_item(
|
||||
&self,
|
||||
list_width: Option<Pixels>,
|
||||
cx: &mut ElementContext,
|
||||
) -> ElementMeasurement {
|
||||
fn measure_item(&self, list_width: Option<Pixels>, cx: &mut ElementContext) -> Size<Pixels> {
|
||||
if self.item_count == 0 {
|
||||
return ElementMeasurement::default();
|
||||
return Size::default();
|
||||
}
|
||||
|
||||
let item_ix = cmp::min(self.item_to_measure_index, self.item_count - 1);
|
||||
@@ -316,7 +278,7 @@ impl UniformList {
|
||||
}),
|
||||
AvailableSpace::MinContent,
|
||||
);
|
||||
item_to_measure.layout(available_space, cx)
|
||||
item_to_measure.measure(available_space, cx)
|
||||
}
|
||||
|
||||
/// Track and render scroll state of this list with reference to the given scroll handle.
|
||||
|
||||
@@ -151,9 +151,7 @@ pub(crate) trait Platform: 'static {
|
||||
fn set_cursor_style(&self, style: CursorStyle);
|
||||
fn should_auto_hide_scrollbars(&self) -> bool;
|
||||
|
||||
fn write_to_primary(&self, item: ClipboardItem);
|
||||
fn write_to_clipboard(&self, item: ClipboardItem);
|
||||
fn read_from_primary(&self) -> Option<ClipboardItem>;
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||
|
||||
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>>;
|
||||
|
||||
@@ -79,14 +79,8 @@ impl LinuxClient for HeadlessClient {
|
||||
//todo(linux)
|
||||
fn set_cursor_style(&self, _style: CursorStyle) {}
|
||||
|
||||
fn write_to_primary(&self, item: crate::ClipboardItem) {}
|
||||
|
||||
fn write_to_clipboard(&self, item: crate::ClipboardItem) {}
|
||||
|
||||
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
||||
None
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -56,9 +56,7 @@ pub trait LinuxClient {
|
||||
options: WindowParams,
|
||||
) -> Box<dyn PlatformWindow>;
|
||||
fn set_cursor_style(&self, style: CursorStyle);
|
||||
fn write_to_primary(&self, item: ClipboardItem);
|
||||
fn write_to_clipboard(&self, item: ClipboardItem);
|
||||
fn read_from_primary(&self) -> Option<ClipboardItem>;
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||
fn run(&self);
|
||||
}
|
||||
@@ -408,6 +406,7 @@ impl<P: LinuxClient + 'static> Platform for P {
|
||||
})
|
||||
}
|
||||
|
||||
//todo(linux): add trait methods for accessing the primary selection
|
||||
fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
|
||||
let url = url.to_string();
|
||||
self.background_executor().spawn(async move {
|
||||
@@ -462,18 +461,10 @@ impl<P: LinuxClient + 'static> Platform for P {
|
||||
Task::ready(Err(anyhow!("register_url_scheme unimplemented")))
|
||||
}
|
||||
|
||||
fn write_to_primary(&self, item: ClipboardItem) {
|
||||
self.write_to_primary(item)
|
||||
}
|
||||
|
||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
self.write_to_clipboard(item)
|
||||
}
|
||||
|
||||
fn read_from_primary(&self) -> Option<ClipboardItem> {
|
||||
self.read_from_primary()
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
self.read_from_clipboard()
|
||||
}
|
||||
|
||||
@@ -118,8 +118,8 @@ pub(crate) struct WaylandClientState {
|
||||
loop_handle: LoopHandle<'static, WaylandClientStatePtr>,
|
||||
cursor_icon_name: String,
|
||||
cursor: Cursor,
|
||||
clipboard: Option<Clipboard>,
|
||||
primary: Option<Primary>,
|
||||
clipboard: Clipboard,
|
||||
primary: Primary,
|
||||
event_loop: Option<EventLoop<'static, WaylandClientStatePtr>>,
|
||||
common: LinuxCommon,
|
||||
}
|
||||
@@ -163,12 +163,6 @@ impl WaylandClientStatePtr {
|
||||
state.mouse_focused_window = Some(window);
|
||||
}
|
||||
}
|
||||
if state.windows.is_empty() {
|
||||
// Drop the clipboard to prevent a seg fault after we've closed all Wayland connections.
|
||||
state.clipboard = None;
|
||||
state.primary = None;
|
||||
state.common.signal.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,8 +278,8 @@ impl WaylandClient {
|
||||
cursor_icon_name: "arrow".to_string(),
|
||||
enter_token: None,
|
||||
cursor,
|
||||
clipboard: Some(clipboard),
|
||||
primary: Some(primary),
|
||||
clipboard,
|
||||
primary,
|
||||
event_loop: Some(event_loop),
|
||||
}));
|
||||
|
||||
@@ -383,44 +377,14 @@ impl LinuxClient for WaylandClient {
|
||||
.log_err();
|
||||
}
|
||||
|
||||
fn write_to_primary(&self, item: crate::ClipboardItem) {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.primary
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_contents(item.text);
|
||||
}
|
||||
|
||||
fn write_to_clipboard(&self, item: crate::ClipboardItem) {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.clipboard
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.set_contents(item.text);
|
||||
}
|
||||
|
||||
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.primary
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_contents()
|
||||
.ok()
|
||||
.map(|s| crate::ClipboardItem {
|
||||
text: s,
|
||||
metadata: None,
|
||||
})
|
||||
self.0.borrow_mut().clipboard.set_contents(item.text);
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.clipboard
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.get_contents()
|
||||
.ok()
|
||||
.map(|s| crate::ClipboardItem {
|
||||
|
||||
@@ -68,7 +68,6 @@ unsafe impl HasRawDisplayHandle for RawWindow {
|
||||
pub struct WaylandWindowState {
|
||||
xdg_surface: xdg_surface::XdgSurface,
|
||||
pub surface: wl_surface::WlSurface,
|
||||
decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
|
||||
toplevel: xdg_toplevel::XdgToplevel,
|
||||
viewport: Option<wp_viewport::WpViewport>,
|
||||
outputs: HashSet<ObjectId>,
|
||||
@@ -91,13 +90,11 @@ pub struct WaylandWindowStatePtr {
|
||||
}
|
||||
|
||||
impl WaylandWindowState {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
surface: wl_surface::WlSurface,
|
||||
xdg_surface: xdg_surface::XdgSurface,
|
||||
toplevel: xdg_toplevel::XdgToplevel,
|
||||
decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,
|
||||
viewport: Option<wp_viewport::WpViewport>,
|
||||
toplevel: xdg_toplevel::XdgToplevel,
|
||||
client: WaylandClientStatePtr,
|
||||
globals: Globals,
|
||||
options: WindowParams,
|
||||
@@ -135,7 +132,6 @@ impl WaylandWindowState {
|
||||
Self {
|
||||
xdg_surface,
|
||||
surface,
|
||||
decoration,
|
||||
toplevel,
|
||||
viewport,
|
||||
globals,
|
||||
@@ -162,27 +158,16 @@ impl Drop for WaylandWindow {
|
||||
let mut state = self.0.state.borrow_mut();
|
||||
let surface_id = state.surface.id();
|
||||
let client = state.client.clone();
|
||||
|
||||
state.renderer.destroy();
|
||||
if let Some(decoration) = &state.decoration {
|
||||
decoration.destroy();
|
||||
}
|
||||
state.toplevel.destroy();
|
||||
if let Some(viewport) = &state.viewport {
|
||||
viewport.destroy();
|
||||
}
|
||||
state.xdg_surface.destroy();
|
||||
state.surface.destroy();
|
||||
|
||||
let state_ptr = self.0.clone();
|
||||
state
|
||||
.globals
|
||||
.executor
|
||||
.spawn(async move {
|
||||
state_ptr.close();
|
||||
client.drop_window(&surface_id)
|
||||
})
|
||||
.detach();
|
||||
state.globals.executor.spawn(async move {
|
||||
state_ptr.close();
|
||||
client.drop_window(&surface_id)
|
||||
});
|
||||
drop(state);
|
||||
}
|
||||
}
|
||||
@@ -212,18 +197,13 @@ impl WaylandWindow {
|
||||
}
|
||||
|
||||
// Attempt to set up window decorations based on the requested configuration
|
||||
let decoration = globals
|
||||
.decoration_manager
|
||||
.as_ref()
|
||||
.map(|decoration_manager| {
|
||||
let decoration = decoration_manager.get_toplevel_decoration(
|
||||
&toplevel,
|
||||
&globals.qh,
|
||||
surface.id(),
|
||||
);
|
||||
decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ClientSide);
|
||||
decoration
|
||||
});
|
||||
if let Some(decoration_manager) = globals.decoration_manager.as_ref() {
|
||||
let decoration =
|
||||
decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id());
|
||||
|
||||
// Request client side decorations if possible
|
||||
decoration.set_mode(zxdg_toplevel_decoration_v1::Mode::ClientSide);
|
||||
}
|
||||
|
||||
let viewport = globals
|
||||
.viewporter
|
||||
@@ -236,9 +216,8 @@ impl WaylandWindow {
|
||||
state: Rc::new(RefCell::new(WaylandWindowState::new(
|
||||
surface.clone(),
|
||||
xdg_surface,
|
||||
toplevel,
|
||||
decoration,
|
||||
viewport,
|
||||
toplevel,
|
||||
client,
|
||||
globals,
|
||||
params,
|
||||
@@ -340,7 +319,7 @@ impl WaylandWindowStatePtr {
|
||||
}
|
||||
result
|
||||
} else {
|
||||
true
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
@@ -502,12 +481,13 @@ impl WaylandWindowStatePtr {
|
||||
}
|
||||
}
|
||||
if let PlatformInput::KeyDown(event) = input {
|
||||
if let Some(ime_key) = &event.keystroke.ime_key {
|
||||
let mut state = self.state.borrow_mut();
|
||||
if let Some(mut input_handler) = state.input_handler.take() {
|
||||
let mut state = self.state.borrow_mut();
|
||||
if let Some(mut input_handler) = state.input_handler.take() {
|
||||
if let Some(ime_key) = &event.keystroke.ime_key {
|
||||
drop(state);
|
||||
input_handler.replace_text_in_range(None, ime_key);
|
||||
self.state.borrow_mut().input_handler = Some(input_handler);
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.input_handler = Some(input_handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@ use util::ResultExt;
|
||||
use x11rb::connection::{Connection, RequestConnection};
|
||||
use x11rb::errors::ConnectionError;
|
||||
use x11rb::protocol::randr::ConnectionExt as _;
|
||||
use x11rb::protocol::xinput::ConnectionExt;
|
||||
use x11rb::protocol::xkb::ConnectionExt as _;
|
||||
use x11rb::protocol::xproto::ConnectionExt as _;
|
||||
use x11rb::protocol::{randr, xinput, xkb, xproto, Event};
|
||||
use x11rb::protocol::{randr, xkb, xproto, Event};
|
||||
use x11rb::xcb_ffi::XCBConnection;
|
||||
use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
|
||||
use xkbcommon::xkb as xkbc;
|
||||
@@ -64,10 +63,6 @@ pub struct X11ClientState {
|
||||
pub(crate) focused_window: Option<xproto::Window>,
|
||||
pub(crate) xkb: xkbc::State,
|
||||
|
||||
pub(crate) scroll_devices: Vec<xinput::DeviceInfo>,
|
||||
pub(crate) scroll_x: Option<f32>,
|
||||
pub(crate) scroll_y: Option<f32>,
|
||||
|
||||
pub(crate) common: LinuxCommon,
|
||||
pub(crate) clipboard: X11ClipboardContext<Clipboard>,
|
||||
pub(crate) primary: X11ClipboardContext<Primary>,
|
||||
@@ -97,19 +92,6 @@ impl X11Client {
|
||||
xcb_connection
|
||||
.prefetch_extension_information(randr::X11_EXTENSION_NAME)
|
||||
.unwrap();
|
||||
xcb_connection
|
||||
.prefetch_extension_information(xinput::X11_EXTENSION_NAME)
|
||||
.unwrap();
|
||||
|
||||
let xinput_version = xcb_connection
|
||||
.xinput_xi_query_version(2, 0)
|
||||
.unwrap()
|
||||
.reply()
|
||||
.unwrap();
|
||||
assert!(
|
||||
xinput_version.major_version >= 2,
|
||||
"XInput Extension v2 not supported."
|
||||
);
|
||||
|
||||
let atoms = XcbAtoms::new(&xcb_connection).unwrap();
|
||||
let xkb = xcb_connection
|
||||
@@ -143,46 +125,6 @@ impl X11Client {
|
||||
xkbc::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id)
|
||||
};
|
||||
|
||||
let device_list = xcb_connection
|
||||
.xinput_list_input_devices()
|
||||
.unwrap()
|
||||
.reply()
|
||||
.unwrap();
|
||||
let scroll_devices = device_list
|
||||
.devices
|
||||
.iter()
|
||||
.scan(0, |class_info_idx, device_info| {
|
||||
Some(*class_info_idx + device_info.num_class_info as usize)
|
||||
})
|
||||
.zip(device_list.devices.iter())
|
||||
.map(|(class_info_idx, device_info)| {
|
||||
(
|
||||
device_info,
|
||||
device_list.infos
|
||||
[(class_info_idx - device_info.num_class_info as usize)..(class_info_idx)]
|
||||
.to_vec(),
|
||||
)
|
||||
})
|
||||
.filter(|(device_info, class_info)| {
|
||||
device_info.device_use == xinput::DeviceUse::IS_X_EXTENSION_POINTER
|
||||
&& class_info.iter().any(|class_info| match class_info.info {
|
||||
xinput::InputInfoInfo::Valuator(xinput::InputInfoInfoValuator {
|
||||
mode,
|
||||
..
|
||||
}) => mode == xinput::ValuatorMode::RELATIVE,
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
.map(|(device_info, _)| *device_info)
|
||||
.collect::<Vec<_>>();
|
||||
for device in &scroll_devices {
|
||||
xcb_connection
|
||||
.xinput_open_device(device.device_id)
|
||||
.unwrap()
|
||||
.reply()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let clipboard = X11ClipboardContext::<Clipboard>::new().unwrap();
|
||||
let primary = X11ClipboardContext::<Primary>::new().unwrap();
|
||||
|
||||
@@ -224,11 +166,6 @@ impl X11Client {
|
||||
windows: HashMap::default(),
|
||||
focused_window: None,
|
||||
xkb: xkb_state,
|
||||
|
||||
scroll_devices,
|
||||
scroll_x: None,
|
||||
scroll_y: None,
|
||||
|
||||
clipboard,
|
||||
primary,
|
||||
})))
|
||||
@@ -377,58 +314,22 @@ impl X11Client {
|
||||
click_count: current_count,
|
||||
first_mouse: false,
|
||||
}));
|
||||
} else if event.detail >= 4 && event.detail <= 5 {
|
||||
// https://stackoverflow.com/questions/15510472/scrollwheel-event-in-x11
|
||||
let scroll_direction = if event.detail == 4 { 1.0 } else { -1.0 };
|
||||
let scroll_y = SCROLL_LINES * scroll_direction;
|
||||
|
||||
drop(state);
|
||||
window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent {
|
||||
position,
|
||||
delta: ScrollDelta::Lines(Point::new(0.0, scroll_y as f32)),
|
||||
modifiers,
|
||||
touch_phase: TouchPhase::Moved,
|
||||
}));
|
||||
} else {
|
||||
log::warn!("Unknown button press: {event:?}");
|
||||
}
|
||||
}
|
||||
Event::XinputMotion(event) => {
|
||||
let window = self.get_window(event.event)?;
|
||||
|
||||
let position = Point::new(
|
||||
(event.event_x as f32 / u16::MAX as f32).into(),
|
||||
(event.event_y as f32 / u16::MAX as f32).into(),
|
||||
);
|
||||
|
||||
let axisvalues = event
|
||||
.axisvalues
|
||||
.iter()
|
||||
.map(|axisvalue| {
|
||||
axisvalue.integral as f32 + axisvalue.frac as f32 / u32::MAX as f32
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if event.valuator_mask[0] & 4 == 4 {
|
||||
let new_scroll = axisvalues[0];
|
||||
let old_scroll = self.0.borrow().scroll_x;
|
||||
self.0.borrow_mut().scroll_x = Some(new_scroll);
|
||||
|
||||
if let Some(old_scroll) = old_scroll {
|
||||
let delta_scroll = (old_scroll - new_scroll).into();
|
||||
window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent {
|
||||
position,
|
||||
delta: ScrollDelta::Pixels(Point::new(delta_scroll, 0.0.into())),
|
||||
modifiers: crate::Modifiers::none(),
|
||||
touch_phase: TouchPhase::default(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if event.valuator_mask[0] & 8 == 8 {
|
||||
let new_scroll = axisvalues[0] / 2.0;
|
||||
let old_scroll = self.0.borrow().scroll_y;
|
||||
self.0.borrow_mut().scroll_y = Some(new_scroll);
|
||||
|
||||
if let Some(old_scroll) = old_scroll {
|
||||
let delta_scroll = (old_scroll - new_scroll).into();
|
||||
window.handle_input(PlatformInput::ScrollWheel(crate::ScrollWheelEvent {
|
||||
position,
|
||||
delta: ScrollDelta::Pixels(Point::new(0.0.into(), delta_scroll)),
|
||||
modifiers: crate::Modifiers::none(),
|
||||
touch_phase: TouchPhase::default(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::ButtonRelease(event) => {
|
||||
let window = self.get_window(event.event)?;
|
||||
let state = self.0.borrow();
|
||||
@@ -528,7 +429,6 @@ impl LinuxClient for X11Client {
|
||||
state.x_root_index,
|
||||
x_window,
|
||||
&state.atoms,
|
||||
&state.scroll_devices,
|
||||
);
|
||||
|
||||
let screen_resources = state
|
||||
@@ -603,26 +503,10 @@ impl LinuxClient for X11Client {
|
||||
//todo(linux)
|
||||
fn set_cursor_style(&self, _style: CursorStyle) {}
|
||||
|
||||
fn write_to_primary(&self, item: crate::ClipboardItem) {
|
||||
self.0.borrow_mut().primary.set_contents(item.text);
|
||||
}
|
||||
|
||||
fn write_to_clipboard(&self, item: crate::ClipboardItem) {
|
||||
self.0.borrow_mut().clipboard.set_contents(item.text);
|
||||
}
|
||||
|
||||
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
.primary
|
||||
.get_contents()
|
||||
.ok()
|
||||
.map(|text| crate::ClipboardItem {
|
||||
text,
|
||||
metadata: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
|
||||
self.0
|
||||
.borrow_mut()
|
||||
|
||||
@@ -13,10 +13,7 @@ use raw_window_handle as rwh;
|
||||
use util::ResultExt;
|
||||
use x11rb::{
|
||||
connection::Connection,
|
||||
protocol::{
|
||||
xinput,
|
||||
xproto::{self, ConnectionExt as _, CreateWindowAux},
|
||||
},
|
||||
protocol::xproto::{self, ConnectionExt as _, CreateWindowAux},
|
||||
wrapper::ConnectionExt,
|
||||
xcb_ffi::XCBConnection,
|
||||
};
|
||||
@@ -143,7 +140,6 @@ impl X11WindowState {
|
||||
x_main_screen_index: usize,
|
||||
x_window: xproto::Window,
|
||||
atoms: &XcbAtoms,
|
||||
scroll_devices: &Vec<xinput::DeviceInfo>,
|
||||
) -> Self {
|
||||
let x_screen_index = params
|
||||
.display_id
|
||||
@@ -164,6 +160,8 @@ impl X11WindowState {
|
||||
| xproto::EventMask::BUTTON1_MOTION
|
||||
| xproto::EventMask::BUTTON2_MOTION
|
||||
| xproto::EventMask::BUTTON3_MOTION
|
||||
| xproto::EventMask::BUTTON4_MOTION
|
||||
| xproto::EventMask::BUTTON5_MOTION
|
||||
| xproto::EventMask::BUTTON_MOTION,
|
||||
);
|
||||
|
||||
@@ -183,20 +181,6 @@ impl X11WindowState {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for device in scroll_devices {
|
||||
xinput::ConnectionExt::xinput_xi_select_events(
|
||||
&xcb_connection,
|
||||
x_window,
|
||||
&[xinput::EventMask {
|
||||
deviceid: device.device_id as u16,
|
||||
mask: vec![xinput::XIEventMask::MOTION],
|
||||
}],
|
||||
)
|
||||
.unwrap()
|
||||
.check()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(titlebar) = params.titlebar {
|
||||
if let Some(title) = titlebar.title {
|
||||
xcb_connection
|
||||
@@ -278,7 +262,6 @@ impl X11Window {
|
||||
x_main_screen_index: usize,
|
||||
x_window: xproto::Window,
|
||||
atoms: &XcbAtoms,
|
||||
scroll_devices: &Vec<xinput::DeviceInfo>,
|
||||
) -> Self {
|
||||
X11Window {
|
||||
state: Rc::new(RefCell::new(X11WindowState::new(
|
||||
@@ -287,7 +270,6 @@ impl X11Window {
|
||||
x_main_screen_index,
|
||||
x_window,
|
||||
atoms,
|
||||
scroll_devices,
|
||||
))),
|
||||
callbacks: Rc::new(RefCell::new(Callbacks::default())),
|
||||
xcb_connection: xcb_connection.clone(),
|
||||
|
||||
@@ -849,8 +849,6 @@ impl Platform for MacPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_to_primary(&self, _item: ClipboardItem) {}
|
||||
|
||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
let state = self.0.lock();
|
||||
unsafe {
|
||||
@@ -888,10 +886,6 @@ impl Platform for MacPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_from_primary(&self) -> Option<ClipboardItem> {
|
||||
None
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
let state = self.0.lock();
|
||||
unsafe {
|
||||
|
||||
@@ -337,6 +337,7 @@ struct MacWindowState {
|
||||
handle: AnyWindowHandle,
|
||||
executor: ForegroundExecutor,
|
||||
native_window: id,
|
||||
native_window_was_closed: bool,
|
||||
native_view: NonNull<Object>,
|
||||
display_link: Option<DisplayLink>,
|
||||
renderer: renderer::Renderer,
|
||||
@@ -604,10 +605,6 @@ impl MacWindow {
|
||||
registerForDraggedTypes:
|
||||
NSArray::arrayWithObject(nil, NSFilenamesPboardType)
|
||||
];
|
||||
let () = msg_send![
|
||||
native_window,
|
||||
setReleasedWhenClosed: NO
|
||||
];
|
||||
|
||||
let native_view: id = msg_send![VIEW_CLASS, alloc];
|
||||
let native_view = NSView::init(native_view);
|
||||
@@ -625,6 +622,7 @@ impl MacWindow {
|
||||
handle,
|
||||
executor,
|
||||
native_window,
|
||||
native_window_was_closed: false,
|
||||
native_view: NonNull::new_unchecked(native_view),
|
||||
display_link: None,
|
||||
renderer: renderer::new_renderer(
|
||||
@@ -772,17 +770,19 @@ impl Drop for MacWindow {
|
||||
this.renderer.destroy();
|
||||
let window = this.native_window;
|
||||
this.display_link.take();
|
||||
unsafe {
|
||||
this.native_window.setDelegate_(nil);
|
||||
if !this.native_window_was_closed {
|
||||
unsafe {
|
||||
this.native_window.setDelegate_(nil);
|
||||
}
|
||||
|
||||
this.executor
|
||||
.spawn(async move {
|
||||
unsafe {
|
||||
window.close();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
this.executor
|
||||
.spawn(async move {
|
||||
unsafe {
|
||||
window.close();
|
||||
window.autorelease();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1592,6 +1592,7 @@ extern "C" fn close_window(this: &Object, _: Sel) {
|
||||
let close_callback = {
|
||||
let window_state = get_window_state(this);
|
||||
let mut lock = window_state.as_ref().lock();
|
||||
lock.native_window_was_closed = true;
|
||||
lock.close_callback.take()
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ pub(crate) struct TestPlatform {
|
||||
active_display: Rc<dyn PlatformDisplay>,
|
||||
active_cursor: Mutex<CursorStyle>,
|
||||
current_clipboard_item: Mutex<Option<ClipboardItem>>,
|
||||
current_primary_item: Mutex<Option<ClipboardItem>>,
|
||||
pub(crate) prompts: RefCell<TestPrompts>,
|
||||
pub opened_url: RefCell<Option<String>>,
|
||||
weak: Weak<Self>,
|
||||
@@ -45,7 +44,6 @@ impl TestPlatform {
|
||||
active_display: Rc::new(TestDisplay::new()),
|
||||
active_window: Default::default(),
|
||||
current_clipboard_item: Mutex::new(None),
|
||||
current_primary_item: Mutex::new(None),
|
||||
weak: weak.clone(),
|
||||
opened_url: Default::default(),
|
||||
})
|
||||
@@ -127,11 +125,8 @@ impl Platform for TestPlatform {
|
||||
#[cfg(target_os = "macos")]
|
||||
return Arc::new(crate::platform::mac::MacTextSystem::new());
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
return Arc::new(crate::platform::cosmic_text::CosmicTextSystem::new());
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
return Arc::new(crate::platform::windows::DirectWriteTextSystem::new().unwrap());
|
||||
}
|
||||
|
||||
fn run(&self, _on_finish_launching: Box<dyn FnOnce()>) {
|
||||
@@ -284,18 +279,10 @@ impl Platform for TestPlatform {
|
||||
false
|
||||
}
|
||||
|
||||
fn write_to_primary(&self, item: ClipboardItem) {
|
||||
*self.current_primary_item.lock() = Some(item);
|
||||
}
|
||||
|
||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
*self.current_clipboard_item.lock() = Some(item);
|
||||
}
|
||||
|
||||
fn read_from_primary(&self) -> Option<ClipboardItem> {
|
||||
self.current_primary_item.lock().clone()
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
self.current_clipboard_item.lock().clone()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
mod direct_write;
|
||||
mod dispatcher;
|
||||
mod display;
|
||||
mod platform;
|
||||
mod util;
|
||||
mod window;
|
||||
|
||||
pub(crate) use direct_write::*;
|
||||
pub(crate) use dispatcher::*;
|
||||
pub(crate) use display::*;
|
||||
pub(crate) use platform::*;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,7 @@ pub(crate) struct WindowsPlatformInner {
|
||||
background_executor: BackgroundExecutor,
|
||||
pub(crate) foreground_executor: ForegroundExecutor,
|
||||
main_receiver: flume::Receiver<Runnable>,
|
||||
text_system: Arc<dyn PlatformTextSystem>,
|
||||
text_system: Arc<CosmicTextSystem>,
|
||||
callbacks: Mutex<Callbacks>,
|
||||
pub raw_window_handles: RwLock<SmallVec<[HWND; 4]>>,
|
||||
pub(crate) dispatch_event: OwnedHandle,
|
||||
@@ -155,13 +155,7 @@ impl WindowsPlatform {
|
||||
let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, dispatch_event.to_raw()));
|
||||
let background_executor = BackgroundExecutor::new(dispatcher.clone());
|
||||
let foreground_executor = ForegroundExecutor::new(dispatcher);
|
||||
let text_system = if let Some(direct_write) = DirectWriteTextSystem::new().log_err() {
|
||||
log::info!("Using direct write text system.");
|
||||
Arc::new(direct_write) as Arc<dyn PlatformTextSystem>
|
||||
} else {
|
||||
log::info!("Using cosmic text system.");
|
||||
Arc::new(CosmicTextSystem::new()) as Arc<dyn PlatformTextSystem>
|
||||
};
|
||||
let text_system = Arc::new(CosmicTextSystem::new());
|
||||
let callbacks = Mutex::new(Callbacks::default());
|
||||
let raw_window_handles = RwLock::new(SmallVec::new());
|
||||
let settings = RefCell::new(WindowsPlatformSystemSettings::new());
|
||||
@@ -692,8 +686,6 @@ impl Platform for WindowsPlatform {
|
||||
false
|
||||
}
|
||||
|
||||
fn write_to_primary(&self, _item: ClipboardItem) {}
|
||||
|
||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
if item.text.len() > 0 {
|
||||
let mut ctx = ClipboardContext::new().unwrap();
|
||||
@@ -701,10 +693,6 @@ impl Platform for WindowsPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_from_primary(&self) -> Option<ClipboardItem> {
|
||||
None
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
let mut ctx = ClipboardContext::new().unwrap();
|
||||
let content = ctx.get_contents().unwrap();
|
||||
|
||||
@@ -284,6 +284,7 @@ impl WindowsWindowInner {
|
||||
WM_CHAR => self.handle_char_msg(msg, wparam, lparam),
|
||||
WM_IME_STARTCOMPOSITION => self.handle_ime_position(),
|
||||
WM_IME_COMPOSITION => self.handle_ime_composition(lparam),
|
||||
WM_IME_CHAR => self.handle_ime_char(wparam),
|
||||
WM_SETCURSOR => self.handle_set_cursor(lparam),
|
||||
_ => None,
|
||||
};
|
||||
@@ -781,6 +782,7 @@ impl WindowsWindowInner {
|
||||
let string_len = ImmGetCompositionStringW(ctx, GCS_COMPSTR, None, 0);
|
||||
let result = if string_len >= 0 {
|
||||
let mut buffer = vec![0u8; string_len as usize + 2];
|
||||
// let mut buffer = [0u8; MAX_PATH as _];
|
||||
ImmGetCompositionStringW(
|
||||
ctx,
|
||||
GCS_COMPSTR,
|
||||
@@ -810,32 +812,6 @@ impl WindowsWindowInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ime_compostion_result(&self) -> Option<String> {
|
||||
unsafe {
|
||||
let ctx = ImmGetContext(self.hwnd);
|
||||
let string_len = ImmGetCompositionStringW(ctx, GCS_RESULTSTR, None, 0);
|
||||
let result = if string_len >= 0 {
|
||||
let mut buffer = vec![0u8; string_len as usize + 2];
|
||||
ImmGetCompositionStringW(
|
||||
ctx,
|
||||
GCS_RESULTSTR,
|
||||
Some(buffer.as_mut_ptr() as _),
|
||||
string_len as _,
|
||||
);
|
||||
let wstring = std::slice::from_raw_parts::<u16>(
|
||||
buffer.as_mut_ptr().cast::<u16>(),
|
||||
string_len as usize / 2,
|
||||
);
|
||||
let string = String::from_utf16_lossy(wstring);
|
||||
Some(string)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ImmReleaseContext(self.hwnd, ctx);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_ime_composition(&self, lparam: LPARAM) -> Option<isize> {
|
||||
let mut ime_input = None;
|
||||
if lparam.0 as u32 & GCS_COMPSTR.0 > 0 {
|
||||
@@ -864,22 +840,31 @@ impl WindowsWindowInner {
|
||||
input_handler.replace_and_mark_text_in_range(None, comp_string, Some(0..caret_pos));
|
||||
self.input_handler.set(Some(input_handler));
|
||||
}
|
||||
if lparam.0 as u32 & GCS_RESULTSTR.0 > 0 {
|
||||
let Some(comp_result) = self.parse_ime_compostion_result() else {
|
||||
return None;
|
||||
};
|
||||
let Some(mut input_handler) = self.input_handler.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
input_handler.replace_text_in_range(None, &comp_result);
|
||||
self.input_handler.set(Some(input_handler));
|
||||
self.invalidate_client_area();
|
||||
return Some(0);
|
||||
}
|
||||
// currently, we don't care other stuff
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_ime_char(&self, wparam: WPARAM) -> Option<String> {
|
||||
let src = [wparam.0 as u16];
|
||||
let Ok(first_char) = char::decode_utf16(src).collect::<Vec<_>>()[0] else {
|
||||
return None;
|
||||
};
|
||||
Some(first_char.to_string())
|
||||
}
|
||||
|
||||
fn handle_ime_char(&self, wparam: WPARAM) -> Option<isize> {
|
||||
let Some(ime_char) = self.parse_ime_char(wparam) else {
|
||||
return Some(1);
|
||||
};
|
||||
let Some(mut input_handler) = self.input_handler.take() else {
|
||||
return Some(1);
|
||||
};
|
||||
input_handler.replace_text_in_range(None, &ime_char);
|
||||
self.input_handler.set(Some(input_handler));
|
||||
self.invalidate_client_area();
|
||||
Some(0)
|
||||
}
|
||||
|
||||
fn handle_drag_drop(&self, input: PlatformInput) {
|
||||
let mut callbacks = self.callbacks.borrow_mut();
|
||||
let Some(ref mut func) = callbacks.input else {
|
||||
|
||||
@@ -26,29 +26,6 @@ macro_rules! create_definitions {
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
/// Get the tag name list of the font OpenType features
|
||||
/// only enabled or disabled features are returned
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn tag_value_list(&self) -> Vec<(String, bool)> {
|
||||
let mut result = Vec::new();
|
||||
$(
|
||||
{
|
||||
let value = if (self.enabled & (1 << $idx)) != 0 {
|
||||
Some(true)
|
||||
} else if (self.disabled & (1 << $idx)) != 0 {
|
||||
Some(false)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(enable) = value {
|
||||
let tag_name = stringify!($name).to_owned();
|
||||
result.push((tag_name, enable));
|
||||
}
|
||||
}
|
||||
)*
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for FontFeatures {
|
||||
@@ -117,11 +94,9 @@ macro_rules! create_definitions {
|
||||
let mut map = serializer.serialize_map(None)?;
|
||||
|
||||
$(
|
||||
{
|
||||
let feature = stringify!($name);
|
||||
if let Some(value) = self.$name() {
|
||||
map.serialize_entry(feature, &value)?;
|
||||
}
|
||||
let feature = stringify!($name);
|
||||
if let Some(value) = self.$name() {
|
||||
map.serialize_entry(feature, &value)?;
|
||||
}
|
||||
)*
|
||||
|
||||
@@ -186,5 +161,5 @@ create_definitions!(
|
||||
(swsh, 30),
|
||||
(titl, 31),
|
||||
(tnum, 32),
|
||||
(zero, 33),
|
||||
(zero, 33)
|
||||
);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext,
|
||||
BeforePaintIndex, Bounds, ContentMask, Element, ElementContext, ElementId, Entity, EntityId,
|
||||
Flatten, FocusHandle, FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render,
|
||||
Style, StyleRefinement, TextStyle, ViewContext, VisualContext, WeakModel,
|
||||
seal::Sealed, AfterLayoutIndex, AnyElement, AnyModel, AnyWeakModel, AppContext, Bounds,
|
||||
ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
|
||||
FocusableView, IntoElement, LayoutId, Model, PaintIndex, Pixels, Render, Style,
|
||||
StyleRefinement, TextStyle, ViewContext, VisualContext, WeakModel,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use refineable::Refineable;
|
||||
@@ -24,16 +24,13 @@ impl<V> Sealed for View<V> {}
|
||||
|
||||
struct AnyViewState {
|
||||
after_layout_range: Range<AfterLayoutIndex>,
|
||||
before_paint_range: Range<BeforePaintIndex>,
|
||||
paint_range: Range<PaintIndex>,
|
||||
focus_target_bounds: Option<Bounds<Pixels>>,
|
||||
cache_key: ViewCacheKey,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ViewCacheKey {
|
||||
after_layout_bounds: Bounds<Pixels>,
|
||||
before_paint_bounds: Bounds<Pixels>,
|
||||
bounds: Bounds<Pixels>,
|
||||
content_mask: ContentMask<Pixels>,
|
||||
text_style: TextStyle,
|
||||
}
|
||||
@@ -95,7 +92,6 @@ impl<V: 'static> View<V> {
|
||||
impl<V: Render> Element for View<V> {
|
||||
type BeforeLayout = AnyElement;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
@@ -106,24 +102,14 @@ impl<V: Render> Element for View<V> {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(element.after_layout(cx), ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.set_view_id(self.entity_id());
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
element.before_paint(cx)
|
||||
element.after_layout(cx)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -132,7 +118,6 @@ impl<V: Render> Element for View<V> {
|
||||
_: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
@@ -292,8 +277,7 @@ impl<V: Render> From<View<V>> for AnyView {
|
||||
|
||||
impl Element for AnyView {
|
||||
type BeforeLayout = Option<AnyElement>;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
type AfterLayout = Option<AnyElement>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
if let Some(style) = self.cached_style.as_ref() {
|
||||
@@ -315,16 +299,20 @@ impl Element for AnyView {
|
||||
bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
) -> Option<AnyElement> {
|
||||
cx.set_view_id(self.entity_id());
|
||||
if self.cached_style.is_some() {
|
||||
let focus_target_bounds = cx.with_element_state::<AnyViewState, _>(
|
||||
cx.with_element_state::<AnyViewState, _>(
|
||||
Some(ElementId::View(self.entity_id())),
|
||||
|element_state, cx| {
|
||||
let mut element_state = element_state.unwrap();
|
||||
|
||||
let content_mask = cx.content_mask();
|
||||
let text_style = cx.text_style();
|
||||
|
||||
if let Some(mut element_state) = element_state {
|
||||
if element_state.cache_key.after_layout_bounds == bounds
|
||||
if element_state.cache_key.bounds == bounds
|
||||
&& element_state.cache_key.content_mask == content_mask
|
||||
&& element_state.cache_key.text_style == text_style
|
||||
&& !cx.window.dirty_views.contains(&self.entity_id())
|
||||
&& !cx.window.refreshing
|
||||
@@ -333,106 +321,34 @@ impl Element for AnyView {
|
||||
cx.reuse_after_layout(element_state.after_layout_range.clone());
|
||||
let after_layout_end = cx.after_layout_index();
|
||||
element_state.after_layout_range = after_layout_start..after_layout_end;
|
||||
return (element_state.focus_target_bounds, Some(element_state));
|
||||
return (None, Some(element_state));
|
||||
}
|
||||
}
|
||||
|
||||
let after_layout_start = cx.after_layout_index();
|
||||
let mut rendered_element = (self.render)(self, cx);
|
||||
let element_measurement = rendered_element.layout(bounds.size.into(), cx);
|
||||
*element = Some(rendered_element);
|
||||
let focus_target_bounds =
|
||||
element_measurement
|
||||
.focus_target_bounds
|
||||
.map(|focus_target_bounds| {
|
||||
Bounds::new(
|
||||
bounds.origin + focus_target_bounds.origin,
|
||||
focus_target_bounds.size,
|
||||
)
|
||||
});
|
||||
let mut element = (self.render)(self, cx);
|
||||
element.layout(bounds.origin, bounds.size.into(), cx);
|
||||
let after_layout_end = cx.after_layout_index();
|
||||
|
||||
let view_state = AnyViewState {
|
||||
after_layout_range: after_layout_start..after_layout_end,
|
||||
before_paint_range: BeforePaintIndex::default()
|
||||
..BeforePaintIndex::default(),
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
focus_target_bounds,
|
||||
cache_key: ViewCacheKey {
|
||||
after_layout_bounds: bounds,
|
||||
text_style,
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
|
||||
(focus_target_bounds, Some(view_state))
|
||||
},
|
||||
);
|
||||
(focus_target_bounds, ())
|
||||
} else {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
let bounds = element.as_mut().unwrap().after_layout(cx);
|
||||
(bounds, ())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.set_view_id(self.entity_id());
|
||||
if self.cached_style.is_some() {
|
||||
cx.with_element_state::<AnyViewState, _>(
|
||||
Some(ElementId::View(self.entity_id())),
|
||||
|element_state, cx| {
|
||||
let mut element_state = element_state.unwrap().unwrap();
|
||||
let content_mask = cx.content_mask();
|
||||
|
||||
if let Some(element) = element {
|
||||
let before_paint_start = cx.before_paint_index();
|
||||
cx.with_absolute_element_offset(bounds.origin, |cx| {
|
||||
element.before_paint(cx)
|
||||
});
|
||||
let before_paint_end = cx.before_paint_index();
|
||||
element_state.before_paint_range = before_paint_start..before_paint_end;
|
||||
element_state.cache_key.before_paint_bounds = bounds;
|
||||
element_state.cache_key.content_mask = content_mask;
|
||||
} else if element_state.cache_key.before_paint_bounds == bounds
|
||||
&& element_state.cache_key.content_mask == content_mask
|
||||
{
|
||||
let before_paint_start = cx.before_paint_index();
|
||||
cx.reuse_before_paint(element_state.before_paint_range.clone());
|
||||
let before_paint_end = cx.before_paint_index();
|
||||
element_state.before_paint_range = before_paint_start..before_paint_end;
|
||||
} else {
|
||||
let mut rendered_element = (self.render)(self, cx);
|
||||
let after_layout_start = cx.after_layout_index();
|
||||
rendered_element.layout(bounds.size.into(), cx);
|
||||
let after_layout_end = cx.after_layout_index();
|
||||
|
||||
let before_paint_start = cx.before_paint_index();
|
||||
cx.with_absolute_element_offset(bounds.origin, |cx| {
|
||||
rendered_element.before_paint(cx)
|
||||
});
|
||||
let before_paint_end = cx.before_paint_index();
|
||||
element_state.after_layout_range = after_layout_start..after_layout_end;
|
||||
element_state.before_paint_range = before_paint_start..before_paint_end;
|
||||
element_state.cache_key.before_paint_bounds = bounds;
|
||||
element_state.cache_key.content_mask = content_mask;
|
||||
|
||||
*element = Some(rendered_element);
|
||||
}
|
||||
|
||||
((), Some(element_state))
|
||||
(
|
||||
Some(element),
|
||||
Some(AnyViewState {
|
||||
after_layout_range: after_layout_start..after_layout_end,
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
cache_key: ViewCacheKey {
|
||||
bounds,
|
||||
content_mask,
|
||||
text_style,
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
cx.with_element_id(Some(ElementId::View(self.entity_id())), |cx| {
|
||||
element.as_mut().unwrap().before_paint(cx)
|
||||
let mut element = element.take().unwrap();
|
||||
element.after_layout(cx);
|
||||
Some(element)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -440,9 +356,8 @@ impl Element for AnyView {
|
||||
fn paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
element: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
_: &mut Self::BeforeLayout,
|
||||
element: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
if self.cached_style.is_some() {
|
||||
|
||||
@@ -121,7 +121,7 @@ pub(crate) struct DeferredDraw {
|
||||
text_style_stack: Vec<TextStyleRefinement>,
|
||||
element: Option<AnyElement>,
|
||||
absolute_offset: Point<Pixels>,
|
||||
before_paint_range: Range<BeforePaintIndex>,
|
||||
layout_range: Range<AfterLayoutIndex>,
|
||||
paint_range: Range<PaintIndex>,
|
||||
}
|
||||
|
||||
@@ -146,12 +146,6 @@ pub(crate) struct Frame {
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct AfterLayoutIndex {
|
||||
accessed_element_states_index: usize,
|
||||
line_layout_index: LineLayoutIndex,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct BeforePaintIndex {
|
||||
hitboxes_index: usize,
|
||||
tooltips_index: usize,
|
||||
deferred_draws_index: usize,
|
||||
@@ -405,8 +399,7 @@ impl<'a> ElementContext<'a> {
|
||||
|
||||
// Layout all root elements.
|
||||
let mut root_element = self.window.root_view.as_ref().unwrap().clone().into_any();
|
||||
root_element.layout(self.window.viewport_size.into(), self);
|
||||
root_element.before_paint(self);
|
||||
root_element.layout(Point::default(), self.window.viewport_size.into(), self);
|
||||
|
||||
let mut sorted_deferred_draws =
|
||||
(0..self.window.next_frame.deferred_draws.len()).collect::<SmallVec<[_; 8]>>();
|
||||
@@ -418,15 +411,13 @@ impl<'a> ElementContext<'a> {
|
||||
let mut tooltip_element = None;
|
||||
if let Some(prompt) = self.window.prompt.take() {
|
||||
let mut element = prompt.view.any_view().into_any();
|
||||
element.layout(self.window.viewport_size.into(), self);
|
||||
element.before_paint(self);
|
||||
element.layout(Point::default(), self.window.viewport_size.into(), self);
|
||||
prompt_element = Some(element);
|
||||
self.window.prompt = Some(prompt);
|
||||
} else if let Some(active_drag) = self.app.active_drag.take() {
|
||||
let mut element = active_drag.view.clone().into_any();
|
||||
element.layout(AvailableSpace::min_size(), self);
|
||||
let offset = self.mouse_position() - active_drag.cursor_offset;
|
||||
self.with_element_offset(offset, |cx| element.before_paint(cx));
|
||||
element.layout(offset, AvailableSpace::min_size(), self);
|
||||
active_drag_element = Some(element);
|
||||
self.app.active_drag = Some(active_drag);
|
||||
} else {
|
||||
@@ -455,7 +446,7 @@ impl<'a> ElementContext<'a> {
|
||||
let tooltip_request = tooltip_request.unwrap();
|
||||
let mut element = tooltip_request.tooltip.view.clone().into_any();
|
||||
let mouse_position = tooltip_request.tooltip.mouse_position;
|
||||
let tooltip_size = element.layout(AvailableSpace::min_size(), self).size;
|
||||
let tooltip_size = element.measure(AvailableSpace::min_size(), self);
|
||||
|
||||
let mut tooltip_bounds = Bounds::new(mouse_position + point(px(1.), px(1.)), tooltip_size);
|
||||
let window_bounds = Bounds {
|
||||
@@ -487,7 +478,7 @@ impl<'a> ElementContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.before_paint(cx));
|
||||
self.with_absolute_element_offset(tooltip_bounds.origin, |cx| element.after_layout(cx));
|
||||
|
||||
self.window.tooltip_bounds = Some(TooltipBounds {
|
||||
id: tooltip_request.id,
|
||||
@@ -509,16 +500,16 @@ impl<'a> ElementContext<'a> {
|
||||
.dispatch_tree
|
||||
.set_active_node(deferred_draw.parent_node);
|
||||
|
||||
let layout_start = self.before_paint_index();
|
||||
let layout_start = self.after_layout_index();
|
||||
if let Some(element) = deferred_draw.element.as_mut() {
|
||||
self.with_absolute_element_offset(deferred_draw.absolute_offset, |cx| {
|
||||
element.before_paint(cx)
|
||||
element.after_layout(cx)
|
||||
});
|
||||
} else {
|
||||
self.reuse_before_paint(deferred_draw.before_paint_range.clone());
|
||||
self.reuse_after_layout(deferred_draw.layout_range.clone());
|
||||
}
|
||||
let layout_end = self.before_paint_index();
|
||||
deferred_draw.before_paint_range = layout_start..layout_end;
|
||||
let layout_end = self.after_layout_index();
|
||||
deferred_draw.layout_range = layout_start..layout_end;
|
||||
}
|
||||
assert_eq!(
|
||||
self.window.next_frame.deferred_draws.len(),
|
||||
@@ -557,26 +548,6 @@ impl<'a> ElementContext<'a> {
|
||||
|
||||
pub(crate) fn after_layout_index(&self) -> AfterLayoutIndex {
|
||||
AfterLayoutIndex {
|
||||
accessed_element_states_index: self.window.next_frame.accessed_element_states.len(),
|
||||
line_layout_index: self.window.text_system.layout_index(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reuse_after_layout(&mut self, range: Range<AfterLayoutIndex>) {
|
||||
let window = &mut self.window;
|
||||
window.next_frame.accessed_element_states.extend(
|
||||
window.rendered_frame.accessed_element_states[range.start.accessed_element_states_index
|
||||
..range.end.accessed_element_states_index]
|
||||
.iter()
|
||||
.cloned(),
|
||||
);
|
||||
window
|
||||
.text_system
|
||||
.reuse_layouts(range.start.line_layout_index..range.end.line_layout_index);
|
||||
}
|
||||
|
||||
pub(crate) fn before_paint_index(&self) -> BeforePaintIndex {
|
||||
BeforePaintIndex {
|
||||
hitboxes_index: self.window.next_frame.hitboxes.len(),
|
||||
tooltips_index: self.window.next_frame.tooltip_requests.len(),
|
||||
deferred_draws_index: self.window.next_frame.deferred_draws.len(),
|
||||
@@ -586,7 +557,7 @@ impl<'a> ElementContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reuse_before_paint(&mut self, range: Range<BeforePaintIndex>) {
|
||||
pub(crate) fn reuse_after_layout(&mut self, range: Range<AfterLayoutIndex>) {
|
||||
let window = &mut self.window;
|
||||
window.next_frame.hitboxes.extend(
|
||||
window.rendered_frame.hitboxes[range.start.hitboxes_index..range.end.hitboxes_index]
|
||||
@@ -624,7 +595,7 @@ impl<'a> ElementContext<'a> {
|
||||
priority: deferred_draw.priority,
|
||||
element: None,
|
||||
absolute_offset: deferred_draw.absolute_offset,
|
||||
before_paint_range: deferred_draw.before_paint_range.clone(),
|
||||
layout_range: deferred_draw.layout_range.clone(),
|
||||
paint_range: deferred_draw.paint_range.clone(),
|
||||
}),
|
||||
);
|
||||
@@ -1003,7 +974,7 @@ impl<'a> ElementContext<'a> {
|
||||
assert_eq!(
|
||||
window.draw_phase,
|
||||
DrawPhase::Layout,
|
||||
"defer_draw can only be called during before_layout or before_paint"
|
||||
"defer_draw can only be called during before_layout or after_layout"
|
||||
);
|
||||
let parent_node = window.next_frame.dispatch_tree.active_node_id().unwrap();
|
||||
window.next_frame.deferred_draws.push(DeferredDraw {
|
||||
@@ -1013,7 +984,7 @@ impl<'a> ElementContext<'a> {
|
||||
priority,
|
||||
element: Some(element),
|
||||
absolute_offset,
|
||||
before_paint_range: BeforePaintIndex::default()..BeforePaintIndex::default(),
|
||||
layout_range: AfterLayoutIndex::default()..AfterLayoutIndex::default(),
|
||||
paint_range: PaintIndex::default()..PaintIndex::default(),
|
||||
});
|
||||
}
|
||||
@@ -1426,7 +1397,7 @@ impl<'a> ElementContext<'a> {
|
||||
bounds
|
||||
}
|
||||
|
||||
/// This method should be called during `before_paint`. You can use
|
||||
/// This method should be called during `after_layout`. You can use
|
||||
/// the returned [Hitbox] during `paint` or in an event handler
|
||||
/// to determine whether the inserted hitbox was the topmost.
|
||||
pub fn insert_hitbox(&mut self, bounds: Bounds<Pixels>, opaque: bool) -> Hitbox {
|
||||
|
||||
@@ -155,7 +155,7 @@ impl FocusableView for ImageView {
|
||||
|
||||
impl Render for ImageView {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let checkered_background = |bounds: Bounds<Pixels>, cx: &mut ElementContext| {
|
||||
let checkered_background = |bounds: Bounds<Pixels>, _, cx: &mut ElementContext| {
|
||||
let square_size = 32.0;
|
||||
|
||||
let start_y = bounds.origin.y.0;
|
||||
@@ -190,7 +190,7 @@ impl Render for ImageView {
|
||||
}
|
||||
};
|
||||
|
||||
let checkered_background = canvas(checkered_background)
|
||||
let checkered_background = canvas(|_, _| (), checkered_background)
|
||||
.border_2()
|
||||
.border_color(cx.theme().styles.colors.border)
|
||||
.size_full()
|
||||
|
||||
@@ -15,9 +15,9 @@ pub trait ContextProvider: Send + Sync {
|
||||
/// Builds a specific context to be placed on top of the basic one (replacing all conflicting entries) and to be used for task resolving later.
|
||||
fn build_context(
|
||||
&self,
|
||||
_worktree_abs_path: Option<&Path>,
|
||||
_location: &Location,
|
||||
_cx: &mut AppContext,
|
||||
_: Option<&Path>,
|
||||
_: &Location,
|
||||
_: &mut AppContext,
|
||||
) -> Result<TaskVariables> {
|
||||
Ok(TaskVariables::default())
|
||||
}
|
||||
|
||||
@@ -363,15 +363,14 @@ impl Render for SyntaxTreeView {
|
||||
.text_bg(cx.theme().colors().background).into_any_element();
|
||||
|
||||
rendered = rendered.child(
|
||||
// canvas(
|
||||
// move |bounds, cx| {
|
||||
// list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
// list
|
||||
// },
|
||||
// |_, mut list, cx| list.paint(cx),
|
||||
// )
|
||||
// .size_full(),
|
||||
todo!("replace canvas"),
|
||||
canvas(
|
||||
move |bounds, cx| {
|
||||
list.layout(bounds.origin, bounds.size.into(), cx);
|
||||
list
|
||||
},
|
||||
|_, mut list, cx| list.paint(cx),
|
||||
)
|
||||
.size_full(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,13 @@ pub fn init(
|
||||
config.name.clone(),
|
||||
config.grammar.clone(),
|
||||
config.matcher.clone(),
|
||||
move || Ok((config.clone(), load_queries($name), None)),
|
||||
move || {
|
||||
Ok((
|
||||
config.clone(),
|
||||
load_queries($name),
|
||||
Some(Arc::new(language::BasicContextProvider)),
|
||||
))
|
||||
},
|
||||
);
|
||||
};
|
||||
($name:literal, $adapters:expr) => {
|
||||
@@ -99,7 +105,13 @@ pub fn init(
|
||||
config.name.clone(),
|
||||
config.grammar.clone(),
|
||||
config.matcher.clone(),
|
||||
move || Ok((config.clone(), load_queries($name), None)),
|
||||
move || {
|
||||
Ok((
|
||||
config.clone(),
|
||||
load_queries($name),
|
||||
Some(Arc::new(language::BasicContextProvider)),
|
||||
))
|
||||
},
|
||||
);
|
||||
};
|
||||
($name:literal, $adapters:expr, $context_provider:expr) => {
|
||||
|
||||
@@ -20,5 +20,28 @@
|
||||
(info_string
|
||||
(language) @text.literal))
|
||||
|
||||
; hack to deal with incorrect grammar parsing
|
||||
(atx_heading
|
||||
(block_quote_marker) @punctuation.block_quote_marker
|
||||
)
|
||||
|
||||
; hack to deal with incorrect grammar parsing
|
||||
(paragraph
|
||||
(block_quote_marker) @punctuation.block_quote_marker
|
||||
)
|
||||
|
||||
; hack to deal with incorrect grammar parsing
|
||||
(list_item
|
||||
(block_quote_marker) @punctuation.block_quote_marker
|
||||
)
|
||||
|
||||
(block_quote
|
||||
(block_quote_marker) @punctuation.block_quote_marker
|
||||
)
|
||||
|
||||
(block_quote
|
||||
(paragraph) @text.block_quote
|
||||
)
|
||||
|
||||
(link_destination) @link_uri
|
||||
(link_text) @link_text
|
||||
|
||||
@@ -10727,7 +10727,6 @@ fn serialize_blame_buffer_response(blame: git::blame::Blame) -> proto::BlameBuff
|
||||
entries,
|
||||
messages,
|
||||
permalinks,
|
||||
remote_url: blame.remote_url,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10776,7 +10775,6 @@ fn deserialize_blame_buffer_response(response: proto::BlameBufferResponse) -> gi
|
||||
entries,
|
||||
permalinks,
|
||||
messages,
|
||||
remote_url: response.remote_url,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,10 +79,6 @@ pub struct InlineBlameSettings {
|
||||
///
|
||||
/// Default: 0
|
||||
pub delay_ms: Option<u64>,
|
||||
/// The minimum column number to show the inline blame information at
|
||||
///
|
||||
/// Default: 0
|
||||
pub min_column: Option<u32>,
|
||||
}
|
||||
|
||||
const fn true_value() -> bool {
|
||||
|
||||
@@ -37,7 +37,7 @@ use util::{maybe, NumericPrefixWithSuffix, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
dock::{DockPosition, Panel, PanelEvent},
|
||||
notifications::DetachAndPromptErr,
|
||||
OpenInTerminal, Workspace,
|
||||
Workspace,
|
||||
};
|
||||
|
||||
const PROJECT_PANEL_KEY: &str = "ProjectPanel";
|
||||
@@ -127,6 +127,7 @@ actions!(
|
||||
CopyPath,
|
||||
CopyRelativePath,
|
||||
RevealInFinder,
|
||||
OpenInTerminal,
|
||||
Cut,
|
||||
Paste,
|
||||
Rename,
|
||||
@@ -440,7 +441,9 @@ impl ProjectPanel {
|
||||
.action("New Folder", Box::new(NewDirectory))
|
||||
.separator()
|
||||
.action("Reveal in Finder", Box::new(RevealInFinder))
|
||||
.action("Open in Terminal", Box::new(OpenInTerminal))
|
||||
.when(is_dir, |menu| {
|
||||
menu.action("Open in Terminal…", Box::new(OpenInTerminal))
|
||||
})
|
||||
.when(is_dir, |menu| {
|
||||
menu.separator()
|
||||
.action("Find in Folder…", Box::new(NewSearchInDirectory))
|
||||
@@ -596,10 +599,7 @@ impl ProjectPanel {
|
||||
}
|
||||
|
||||
pub fn collapse_all_entries(&mut self, _: &CollapseAllEntries, cx: &mut ViewContext<Self>) {
|
||||
// By keeping entries for fully collapsed worktrees, we avoid expanding them within update_visible_entries
|
||||
// (which is it's default behaviour when there's no entry for a worktree in expanded_dir_ids).
|
||||
self.expanded_dir_ids
|
||||
.retain(|_, expanded_entries| expanded_entries.is_empty());
|
||||
self.expanded_dir_ids.clear();
|
||||
self.update_visible_entries(None, cx);
|
||||
cx.notify();
|
||||
}
|
||||
@@ -1128,20 +1128,13 @@ impl ProjectPanel {
|
||||
|
||||
fn open_in_terminal(&mut self, _: &OpenInTerminal, cx: &mut ViewContext<Self>) {
|
||||
if let Some((worktree, entry)) = self.selected_entry(cx) {
|
||||
let abs_path = worktree.abs_path().join(&entry.path);
|
||||
let working_directory = if entry.is_dir() {
|
||||
Some(abs_path)
|
||||
} else {
|
||||
if entry.is_symlink {
|
||||
abs_path.canonicalize().ok()
|
||||
} else {
|
||||
Some(abs_path)
|
||||
let path = worktree.abs_path().join(&entry.path);
|
||||
cx.dispatch_action(
|
||||
workspace::OpenTerminal {
|
||||
working_directory: path,
|
||||
}
|
||||
.and_then(|path| Some(path.parent()?.to_path_buf()))
|
||||
};
|
||||
if let Some(working_directory) = working_directory {
|
||||
cx.dispatch_action(workspace::OpenTerminal { working_directory }.boxed_clone())
|
||||
}
|
||||
.boxed_clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1903,10 +1896,8 @@ impl Panel for ProjectPanel {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn icon(&self, cx: &WindowContext) -> Option<IconName> {
|
||||
ProjectPanelSettings::get_global(cx)
|
||||
.button
|
||||
.then(|| IconName::FileTree)
|
||||
fn icon(&self, _: &WindowContext) -> Option<ui::IconName> {
|
||||
Some(ui::IconName::FileTree)
|
||||
}
|
||||
|
||||
fn icon_tooltip(&self, _cx: &WindowContext) -> Option<&'static str> {
|
||||
|
||||
@@ -13,7 +13,6 @@ pub enum ProjectPanelDockPosition {
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ProjectPanelSettings {
|
||||
pub button: bool,
|
||||
pub default_width: Pixels,
|
||||
pub dock: ProjectPanelDockPosition,
|
||||
pub file_icons: bool,
|
||||
@@ -26,10 +25,6 @@ pub struct ProjectPanelSettings {
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
|
||||
pub struct ProjectPanelSettingsContent {
|
||||
/// Whether to show the project panel button in the status bar.
|
||||
///
|
||||
/// Default: true
|
||||
pub button: Option<bool>,
|
||||
/// Customise default width (in pixels) taken by project panel
|
||||
///
|
||||
/// Default: 240
|
||||
|
||||
@@ -1963,7 +1963,6 @@ message BlameBufferResponse {
|
||||
repeated BlameEntry entries = 1;
|
||||
repeated CommitMessage messages = 2;
|
||||
repeated CommitPermalink permalinks = 3;
|
||||
optional string remote_url = 4;
|
||||
}
|
||||
|
||||
message MultiLspQuery {
|
||||
|
||||
@@ -436,7 +436,6 @@ impl BufferSearchBar {
|
||||
pub fn register(registrar: &mut impl SearchActionsRegistrar) {
|
||||
registrar.register_handler(ForDeployed(|this, _: &FocusSearch, cx| {
|
||||
this.query_editor.focus_handle(cx).focus(cx);
|
||||
this.select_query(cx);
|
||||
}));
|
||||
registrar.register_handler(ForDeployed(|this, action: &ToggleCaseSensitive, cx| {
|
||||
if this.supported_options().case {
|
||||
|
||||
@@ -6,9 +6,6 @@ edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/semantic_index.rs"
|
||||
|
||||
@@ -46,3 +43,6 @@ project = { workspace = true, features = ["test-support"] }
|
||||
tempfile.workspace = true
|
||||
util = { workspace = true, features = ["test-support"] }
|
||||
worktree = { workspace = true, features = ["test-support"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -75,26 +75,6 @@ impl ResolvedTask {
|
||||
pub fn substituted_variables(&self) -> &HashSet<VariableName> {
|
||||
&self.substituted_variables
|
||||
}
|
||||
|
||||
/// If the resolution produced a task with the command, returns a string, combined from its command and arguments.
|
||||
pub fn resolved_command(&self) -> Option<String> {
|
||||
self.resolved.as_ref().map(|resolved| {
|
||||
let mut command = resolved.command.clone();
|
||||
for arg in &resolved.args {
|
||||
command.push(' ');
|
||||
command.push_str(arg);
|
||||
}
|
||||
command
|
||||
})
|
||||
}
|
||||
|
||||
/// A human-readable label to display in the UI.
|
||||
pub fn display_label(&self) -> &str {
|
||||
self.resolved
|
||||
.as_ref()
|
||||
.map(|resolved| resolved.label.as_str())
|
||||
.unwrap_or_else(|| self.resolved_label.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Variables, available for use in [`TaskContext`] when a Zed's [`TaskTemplate`] gets resolved into a [`ResolvedTask`].
|
||||
|
||||
@@ -25,6 +25,7 @@ util.workspace = true
|
||||
terminal.workspace = true
|
||||
workspace.workspace = true
|
||||
language.workspace = true
|
||||
itertools.workspace = true
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -168,8 +168,8 @@ fn task_context(workspace: &Workspace, cx: &mut WindowContext<'_>) -> TaskContex
|
||||
let language_context_provider = buffer
|
||||
.read(cx)
|
||||
.language()
|
||||
.and_then(|language| language.context_provider())
|
||||
.unwrap_or_else(|| Arc::new(BasicContextProvider));
|
||||
.and_then(|language| language.context_provider())?;
|
||||
|
||||
let selection_range = selection.range();
|
||||
let start = editor_snapshot
|
||||
.display_snapshot
|
||||
|
||||
@@ -233,7 +233,11 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
.map(|(index, (_, candidate))| StringMatchCandidate {
|
||||
id: index,
|
||||
char_bag: candidate.resolved_label.chars().collect(),
|
||||
string: candidate.display_label().to_owned(),
|
||||
string: candidate
|
||||
.resolved
|
||||
.as_ref()
|
||||
.map(|resolved| resolved.label.clone())
|
||||
.unwrap_or_else(|| candidate.resolved_label.clone()),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
@@ -302,28 +306,7 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
) -> Option<Self::ListItem> {
|
||||
let candidates = self.candidates.as_ref()?;
|
||||
let hit = &self.matches[ix];
|
||||
let (source_kind, resolved_task) = &candidates.get(hit.candidate_id)?;
|
||||
let template = resolved_task.original_task();
|
||||
let display_label = resolved_task.display_label();
|
||||
|
||||
let mut tooltip_label_text = if display_label != &template.label {
|
||||
template.label.clone()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
if let Some(resolved_command) = resolved_task.resolved_command() {
|
||||
if display_label != resolved_command {
|
||||
if !tooltip_label_text.trim().is_empty() {
|
||||
tooltip_label_text.push('\n');
|
||||
}
|
||||
tooltip_label_text.push_str(&resolved_command);
|
||||
}
|
||||
}
|
||||
let tooltip_label = if tooltip_label_text.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Tooltip::text(tooltip_label_text, cx))
|
||||
};
|
||||
let (source_kind, _) = &candidates.get(hit.candidate_id)?;
|
||||
|
||||
let highlighted_location = HighlightedText {
|
||||
text: hit.string.clone(),
|
||||
@@ -338,14 +321,10 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
.get_type_icon(&name.to_lowercase())
|
||||
.map(|icon_path| Icon::from_path(icon_path)),
|
||||
};
|
||||
|
||||
Some(
|
||||
ListItem::new(SharedString::from(format!("tasks-modal-{ix}")))
|
||||
.inset(true)
|
||||
.spacing(ListItemSpacing::Sparse)
|
||||
.when_some(tooltip_label, |list_item, item_label| {
|
||||
list_item.tooltip(move |_| item_label.clone())
|
||||
})
|
||||
.map(|item| {
|
||||
let item = if matches!(source_kind, TaskSourceKind::UserInput)
|
||||
|| Some(ix) <= self.last_used_candidate_index
|
||||
@@ -389,10 +368,18 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
}
|
||||
|
||||
fn selected_as_query(&self) -> Option<String> {
|
||||
use itertools::intersperse;
|
||||
let task_index = self.matches.get(self.selected_index())?.candidate_id;
|
||||
let tasks = self.candidates.as_ref()?;
|
||||
let (_, task) = tasks.get(task_index)?;
|
||||
task.resolved_command()
|
||||
task.resolved.as_ref().map(|spawn_in_terminal| {
|
||||
let mut command = spawn_in_terminal.command.clone();
|
||||
if !spawn_in_terminal.args.is_empty() {
|
||||
command.push(' ');
|
||||
command.extend(intersperse(spawn_in_terminal.args.clone(), " ".to_string()));
|
||||
}
|
||||
command
|
||||
})
|
||||
}
|
||||
|
||||
fn confirm_input(&mut self, omit_history_entry: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||
@@ -418,11 +405,7 @@ impl PickerDelegate for TasksModalDelegate {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use editor::Editor;
|
||||
use gpui::{TestAppContext, VisualTestContext};
|
||||
use language::Point;
|
||||
use project::{FakeFs, Project};
|
||||
use serde_json::json;
|
||||
|
||||
@@ -578,100 +561,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_basic_context_for_simple_files(cx: &mut TestAppContext) {
|
||||
crate::tests::init_test(cx);
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
".zed": {
|
||||
"tasks.json": r#"[
|
||||
{
|
||||
"label": "hello from $ZED_FILE:$ZED_ROW:$ZED_COLUMN",
|
||||
"command": "echo",
|
||||
"args": ["hello", "from", "$ZED_FILE", ":", "$ZED_ROW", ":", "$ZED_COLUMN"]
|
||||
},
|
||||
{
|
||||
"label": "opened now: $ZED_WORKTREE_ROOT",
|
||||
"command": "echo",
|
||||
"args": ["opened", "now:", "$ZED_WORKTREE_ROOT"]
|
||||
}
|
||||
]"#,
|
||||
},
|
||||
"file_without_extension": "aaaaaaaaaaaaaaaaaaaa\naaaaaaaaaaaaaaaaaa",
|
||||
"file_with.odd_extension": "b",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
|
||||
|
||||
let tasks_picker = open_spawn_tasks(&workspace, cx);
|
||||
assert_eq!(
|
||||
task_names(&tasks_picker, cx),
|
||||
Vec::<String>::new(),
|
||||
"Should list no file or worktree context-dependent when no file is open"
|
||||
);
|
||||
tasks_picker.update(cx, |_, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
});
|
||||
drop(tasks_picker);
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
let _ = workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.open_abs_path(PathBuf::from("/dir/file_with.odd_extension"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.executor().run_until_parked();
|
||||
let tasks_picker = open_spawn_tasks(&workspace, cx);
|
||||
assert_eq!(
|
||||
task_names(&tasks_picker, cx),
|
||||
vec![
|
||||
"hello from …th.odd_extension:1:1".to_string(),
|
||||
"opened now: /dir".to_string()
|
||||
],
|
||||
"Second opened buffer should fill the context, labels should be trimmed if long enough"
|
||||
);
|
||||
tasks_picker.update(cx, |_, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
});
|
||||
drop(tasks_picker);
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
let second_item = workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.open_abs_path(PathBuf::from("/dir/file_without_extension"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let editor = cx.update(|cx| second_item.act_as::<Editor>(cx)).unwrap();
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.change_selections(None, cx, |s| {
|
||||
s.select_ranges(Some(Point::new(1, 2)..Point::new(1, 5)))
|
||||
})
|
||||
});
|
||||
cx.executor().run_until_parked();
|
||||
let tasks_picker = open_spawn_tasks(&workspace, cx);
|
||||
assert_eq!(
|
||||
task_names(&tasks_picker, cx),
|
||||
vec![
|
||||
"hello from …ithout_extension:2:3".to_string(),
|
||||
"opened now: /dir".to_string()
|
||||
],
|
||||
"Opened buffer should fill the context, labels should be trimmed if long enough"
|
||||
);
|
||||
tasks_picker.update(cx, |_, cx| {
|
||||
cx.emit(DismissEvent);
|
||||
});
|
||||
drop(tasks_picker);
|
||||
cx.executor().run_until_parked();
|
||||
}
|
||||
|
||||
fn open_spawn_tasks(
|
||||
workspace: &View<Workspace>,
|
||||
cx: &mut VisualTestContext,
|
||||
@@ -680,7 +569,7 @@ mod tests {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
.active_modal::<TasksModal>(cx)
|
||||
.expect("no task modal after `Spawn` action was dispatched")
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.picker
|
||||
.clone()
|
||||
|
||||
@@ -750,11 +750,6 @@ impl Terminal {
|
||||
InternalEvent::SetSelection(selection) => {
|
||||
term.selection = selection.as_ref().map(|(sel, _)| sel.clone());
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(selection_text) = term.selection_to_string() {
|
||||
cx.write_to_primary(ClipboardItem::new(selection_text));
|
||||
}
|
||||
|
||||
if let Some((_, head)) = selection {
|
||||
self.selection_head = Some(*head);
|
||||
}
|
||||
@@ -771,11 +766,6 @@ impl Terminal {
|
||||
selection.update(point, side);
|
||||
term.selection = Some(selection);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
if let Some(selection_text) = term.selection_to_string() {
|
||||
cx.write_to_primary(ClipboardItem::new(selection_text));
|
||||
}
|
||||
|
||||
self.selection_head = Some(point);
|
||||
cx.emit(Event::SelectionsChanged)
|
||||
}
|
||||
@@ -1202,12 +1192,7 @@ impl Terminal {
|
||||
Some(scroll_delta)
|
||||
}
|
||||
|
||||
pub fn mouse_down(
|
||||
&mut self,
|
||||
e: &MouseDownEvent,
|
||||
origin: Point<Pixels>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
pub fn mouse_down(&mut self, e: &MouseDownEvent, origin: Point<Pixels>) {
|
||||
let position = e.position - origin;
|
||||
let point = grid_point(
|
||||
position,
|
||||
@@ -1244,11 +1229,6 @@ impl Terminal {
|
||||
self.events
|
||||
.push_back(InternalEvent::SetSelection(Some((sel, point))));
|
||||
}
|
||||
} else if e.button == MouseButton::Middle {
|
||||
if let Some(item) = cx.read_from_primary() {
|
||||
let text = item.text().to_string();
|
||||
self.input(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ impl TerminalElement {
|
||||
move |e, cx| {
|
||||
cx.focus(&focus);
|
||||
terminal.update(cx, |terminal, cx| {
|
||||
terminal.mouse_down(&e, origin, cx);
|
||||
terminal.mouse_down(&e, origin);
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
@@ -479,17 +479,6 @@ impl TerminalElement {
|
||||
},
|
||||
),
|
||||
);
|
||||
self.interactivity.on_mouse_down(
|
||||
MouseButton::Middle,
|
||||
TerminalElement::generic_button_handler(
|
||||
terminal.clone(),
|
||||
origin,
|
||||
focus.clone(),
|
||||
move |terminal, origin, e, cx| {
|
||||
terminal.mouse_down(&e, origin, cx);
|
||||
},
|
||||
),
|
||||
);
|
||||
self.interactivity.on_scroll_wheel({
|
||||
let terminal = terminal.clone();
|
||||
move |e, cx| {
|
||||
@@ -509,8 +498,19 @@ impl TerminalElement {
|
||||
terminal.clone(),
|
||||
origin,
|
||||
focus.clone(),
|
||||
move |terminal, origin, e, cx| {
|
||||
terminal.mouse_down(&e, origin, cx);
|
||||
move |terminal, origin, e, _cx| {
|
||||
terminal.mouse_down(&e, origin);
|
||||
},
|
||||
),
|
||||
);
|
||||
self.interactivity.on_mouse_down(
|
||||
MouseButton::Middle,
|
||||
TerminalElement::generic_button_handler(
|
||||
terminal.clone(),
|
||||
origin,
|
||||
focus.clone(),
|
||||
move |terminal, origin, e, _cx| {
|
||||
terminal.mouse_down(&e, origin);
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -542,7 +542,7 @@ impl TerminalElement {
|
||||
|
||||
impl Element for TerminalElement {
|
||||
type BeforeLayout = ();
|
||||
type BeforePaint = LayoutState;
|
||||
type AfterLayout = LayoutState;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
self.interactivity.occlude_mouse();
|
||||
@@ -556,14 +556,14 @@ impl Element for TerminalElement {
|
||||
(layout_id, ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Self::BeforePaint {
|
||||
) -> Self::AfterLayout {
|
||||
self.interactivity
|
||||
.before_paint(bounds, bounds.size, cx, |_, _, hitbox, cx| {
|
||||
.after_layout(bounds, bounds.size, cx, |_, _, hitbox, cx| {
|
||||
let hitbox = hitbox.unwrap();
|
||||
let settings = ThemeSettings::get_global(cx).clone();
|
||||
|
||||
@@ -776,7 +776,7 @@ impl Element for TerminalElement {
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
layout: &mut Self::BeforePaint,
|
||||
layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext<'_>,
|
||||
) {
|
||||
cx.paint_quad(fill(bounds, layout.background_color));
|
||||
|
||||
@@ -98,6 +98,7 @@ impl TerminalPanel {
|
||||
.on_click(cx.listener(|pane, _, cx| {
|
||||
pane.toggle_zoom(&workspace::ToggleZoom, cx);
|
||||
}))
|
||||
// TODO kb
|
||||
.tooltip(move |cx| {
|
||||
Tooltip::for_action(
|
||||
if zoomed { "Zoom Out" } else { "Zoom In" },
|
||||
@@ -291,13 +292,13 @@ impl TerminalPanel {
|
||||
action: &workspace::OpenTerminal,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let Some(terminal_panel) = workspace.panel::<Self>(cx) else {
|
||||
let Some(this) = workspace.focus_panel::<Self>(cx) else {
|
||||
return;
|
||||
};
|
||||
terminal_panel.update(cx, |panel, cx| {
|
||||
panel.add_terminal(Some(action.working_directory.clone()), None, cx)
|
||||
});
|
||||
workspace.focus_panel::<Self>(cx);
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
this.add_terminal(Some(action.working_directory.clone()), None, cx)
|
||||
})
|
||||
}
|
||||
|
||||
fn spawn_task(&mut self, spawn_in_terminal: &SpawnInTerminal, cx: &mut ViewContext<Self>) {
|
||||
@@ -426,17 +427,17 @@ impl TerminalPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new Terminal in the current working directory or the user's home directory
|
||||
///Create a new Terminal in the current working directory or the user's home directory
|
||||
fn new_terminal(
|
||||
workspace: &mut Workspace,
|
||||
_: &workspace::NewTerminal,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let Some(terminal_panel) = workspace.panel::<Self>(cx) else {
|
||||
let Some(this) = workspace.focus_panel::<Self>(cx) else {
|
||||
return;
|
||||
};
|
||||
terminal_panel.update(cx, |this, cx| this.add_terminal(None, None, cx));
|
||||
workspace.focus_panel::<Self>(cx);
|
||||
|
||||
this.update(cx, |this, cx| this.add_terminal(None, None, cx))
|
||||
}
|
||||
|
||||
fn terminals_for_task(
|
||||
@@ -597,14 +598,9 @@ impl TerminalPanel {
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn pane(&self) -> &View<Pane> {
|
||||
&self.pane
|
||||
}
|
||||
|
||||
fn has_no_terminals(&mut self, cx: &mut ViewContext<'_, Self>) -> bool {
|
||||
self.pane.read(cx).items_len() == 0 && self.pending_terminals_to_add == 0
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_for_terminals_tasks(
|
||||
@@ -717,7 +713,7 @@ impl Panel for TerminalPanel {
|
||||
}
|
||||
|
||||
fn set_active(&mut self, active: bool, cx: &mut ViewContext<Self>) {
|
||||
if active && self.has_no_terminals(cx) {
|
||||
if active && self.pane.read(cx).items_len() == 0 && self.pending_terminals_to_add == 0 {
|
||||
self.add_terminal(None, None, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ impl TerminalView {
|
||||
self.has_bell
|
||||
}
|
||||
|
||||
pub fn clear_bell(&mut self, cx: &mut ViewContext<TerminalView>) {
|
||||
pub fn clear_bel(&mut self, cx: &mut ViewContext<TerminalView>) {
|
||||
self.has_bell = false;
|
||||
cx.emit(Event::Wakeup);
|
||||
}
|
||||
@@ -332,7 +332,7 @@ impl TerminalView {
|
||||
}
|
||||
|
||||
fn send_text(&mut self, text: &SendText, cx: &mut ViewContext<Self>) {
|
||||
self.clear_bell(cx);
|
||||
self.clear_bel(cx);
|
||||
self.terminal.update(cx, |term, _| {
|
||||
term.input(text.0.to_string());
|
||||
});
|
||||
@@ -340,7 +340,7 @@ impl TerminalView {
|
||||
|
||||
fn send_keystroke(&mut self, text: &SendKeystroke, cx: &mut ViewContext<Self>) {
|
||||
if let Some(keystroke) = Keystroke::parse(&text.0).log_err() {
|
||||
self.clear_bell(cx);
|
||||
self.clear_bel(cx);
|
||||
self.terminal.update(cx, |term, cx| {
|
||||
term.try_keystroke(&keystroke, TerminalSettings::get_global(cx).option_as_meta);
|
||||
});
|
||||
@@ -700,7 +700,7 @@ pub fn regex_search_for_query(query: &project::search::SearchQuery) -> Option<Re
|
||||
|
||||
impl TerminalView {
|
||||
fn key_down(&mut self, event: &KeyDownEvent, cx: &mut ViewContext<Self>) {
|
||||
self.clear_bell(cx);
|
||||
self.clear_bel(cx);
|
||||
self.pause_cursor_blinking(cx);
|
||||
|
||||
self.terminal.update(cx, |term, cx| {
|
||||
|
||||
@@ -169,8 +169,7 @@ pub struct PopoverMenuFrameState {
|
||||
|
||||
impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||
type BeforeLayout = PopoverMenuFrameState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Option<HitboxId>;
|
||||
type AfterLayout = Option<HitboxId>;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
@@ -220,40 +219,14 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||
let mut focus_target_bounds = None;
|
||||
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
|
||||
focus_target_bounds = Some(child_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
if let Some(menu_focus_target_bounds) = menu.after_layout(cx) {
|
||||
focus_target_bounds = Some(menu_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
(focus_target_bounds, ())
|
||||
})
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Option<HitboxId> {
|
||||
self.with_element_state(cx, |_this, element_state, cx| {
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
child.before_paint(cx);
|
||||
child.after_layout(cx);
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
menu.before_paint(cx);
|
||||
menu.after_layout(cx);
|
||||
}
|
||||
|
||||
before_layout.child_layout_id.map(|layout_id| {
|
||||
@@ -268,7 +241,6 @@ impl<M: ManagedView> Element for PopoverMenu<M> {
|
||||
&mut self,
|
||||
_: Bounds<gpui::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
child_hitbox: &mut Option<HitboxId>,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
|
||||
@@ -97,8 +97,7 @@ pub struct MenuHandleFrameState {
|
||||
|
||||
impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||
type BeforeLayout = MenuHandleFrameState;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = Hitbox;
|
||||
type AfterLayout = Hitbox;
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (gpui::LayoutId, Self::BeforeLayout) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
@@ -145,46 +144,20 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||
let mut focus_target_bounds = None;
|
||||
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
if let Some(child_focus_target_bounds) = child.after_layout(cx) {
|
||||
focus_target_bounds = Some(child_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
if let Some(menu_focus_target_bounds) = menu.after_layout(cx) {
|
||||
focus_target_bounds = Some(menu_focus_target_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
(focus_target_bounds, ())
|
||||
})
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> Hitbox {
|
||||
cx.with_element_id(Some(self.id.clone()), |cx| {
|
||||
let hitbox = cx.insert_hitbox(bounds, false);
|
||||
|
||||
if let Some(child) = before_layout.child_element.as_mut() {
|
||||
child.before_paint(cx);
|
||||
child.after_layout(cx);
|
||||
}
|
||||
|
||||
if let Some(menu) = before_layout.menu_element.as_mut() {
|
||||
menu.before_paint(cx);
|
||||
menu.after_layout(cx);
|
||||
}
|
||||
|
||||
hitbox
|
||||
@@ -195,8 +168,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
|
||||
&mut self,
|
||||
_bounds: Bounds<gpui::Pixels>,
|
||||
before_layout: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
hitbox: &mut Self::BeforePaint,
|
||||
hitbox: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
self.with_element_state(cx, |this, element_state, cx| {
|
||||
|
||||
@@ -205,27 +205,23 @@ impl<P> PathLikeWithPosition<P> {
|
||||
column: None,
|
||||
})
|
||||
} else {
|
||||
let (maybe_col_str, _) =
|
||||
maybe_col_str.split_once(':').unwrap_or((maybe_col_str, ""));
|
||||
let maybe_col_str =
|
||||
if maybe_col_str.ends_with(FILE_ROW_COLUMN_DELIMITER) {
|
||||
&maybe_col_str[..maybe_col_str.len() - 1]
|
||||
} else {
|
||||
maybe_col_str
|
||||
};
|
||||
match maybe_col_str.parse::<u32>() {
|
||||
Ok(col) => Ok(Self {
|
||||
path_like: parse_path_like_str(path_like_str)?,
|
||||
row: Some(row),
|
||||
column: Some(col),
|
||||
}),
|
||||
Err(_) => Ok(Self {
|
||||
path_like: parse_path_like_str(path_like_str)?,
|
||||
row: Some(row),
|
||||
column: None,
|
||||
}),
|
||||
Err(_) => fallback(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => Ok(Self {
|
||||
path_like: parse_path_like_str(path_like_str)?,
|
||||
row: None,
|
||||
column: None,
|
||||
}),
|
||||
Err(_) => fallback(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,23 +352,23 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn path_with_position_parsing_negative() {
|
||||
for (input, row, column) in [
|
||||
("test_file.rs:a", None, None),
|
||||
("test_file.rs:a:b", None, None),
|
||||
("test_file.rs::", None, None),
|
||||
("test_file.rs::1", None, None),
|
||||
("test_file.rs:1::", Some(1), None),
|
||||
("test_file.rs::1:2", None, None),
|
||||
("test_file.rs:1::2", Some(1), None),
|
||||
("test_file.rs:1:2:3", Some(1), Some(2)),
|
||||
for input in [
|
||||
"test_file.rs:a",
|
||||
"test_file.rs:a:b",
|
||||
"test_file.rs::",
|
||||
"test_file.rs::1",
|
||||
"test_file.rs:1::",
|
||||
"test_file.rs::1:2",
|
||||
"test_file.rs:1::2",
|
||||
"test_file.rs:1:2:3",
|
||||
] {
|
||||
let actual = parse_str(input);
|
||||
assert_eq!(
|
||||
actual,
|
||||
PathLikeWithPosition {
|
||||
path_like: "test_file.rs".to_string(),
|
||||
row,
|
||||
column,
|
||||
path_like: input.to_string(),
|
||||
row: None,
|
||||
column: None,
|
||||
},
|
||||
"For negative case input str '{input}', got a parse mismatch"
|
||||
);
|
||||
|
||||
@@ -5,8 +5,7 @@ use crate::{
|
||||
},
|
||||
toolbar::Toolbar,
|
||||
workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings},
|
||||
NewCenterTerminal, NewFile, NewSearch, OpenInTerminal, OpenTerminal, OpenVisible,
|
||||
SplitDirection, ToggleZoom, Workspace,
|
||||
NewCenterTerminal, NewFile, NewSearch, OpenVisible, SplitDirection, ToggleZoom, Workspace,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::{HashMap, HashSet, VecDeque};
|
||||
@@ -1598,58 +1597,20 @@ impl Pane {
|
||||
);
|
||||
|
||||
if let Some(entry) = single_entry_to_resolve {
|
||||
let parent_abs_path = pane
|
||||
.update(cx, |pane, cx| {
|
||||
pane.workspace.update(cx, |workspace, cx| {
|
||||
let project = workspace.project().read(cx);
|
||||
project.worktree_for_entry(entry, cx).and_then(|worktree| {
|
||||
let worktree = worktree.read(cx);
|
||||
let entry = worktree.entry_for_id(entry)?;
|
||||
let abs_path = worktree.absolutize(&entry.path).ok()?;
|
||||
let parent = if entry.is_symlink {
|
||||
abs_path.canonicalize().ok()?
|
||||
} else {
|
||||
abs_path
|
||||
}
|
||||
.parent()?
|
||||
.to_path_buf();
|
||||
Some(parent)
|
||||
})
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
.flatten();
|
||||
|
||||
let entry_id = entry.to_proto();
|
||||
menu = menu
|
||||
.separator()
|
||||
.entry(
|
||||
"Reveal In Project Panel",
|
||||
Some(Box::new(RevealInProjectPanel {
|
||||
entry_id: Some(entry_id),
|
||||
})),
|
||||
cx.handler_for(&pane, move |pane, cx| {
|
||||
pane.project.update(cx, |_, cx| {
|
||||
cx.emit(project::Event::RevealInProjectPanel(
|
||||
ProjectEntryId::from_proto(entry_id),
|
||||
))
|
||||
});
|
||||
}),
|
||||
)
|
||||
.when_some(parent_abs_path, |menu, abs_path| {
|
||||
menu.entry(
|
||||
"Open in Terminal",
|
||||
Some(Box::new(OpenInTerminal)),
|
||||
cx.handler_for(&pane, move |_, cx| {
|
||||
cx.dispatch_action(
|
||||
OpenTerminal {
|
||||
working_directory: abs_path.clone(),
|
||||
}
|
||||
.boxed_clone(),
|
||||
);
|
||||
}),
|
||||
)
|
||||
});
|
||||
menu = menu.separator().entry(
|
||||
"Reveal In Project Panel",
|
||||
Some(Box::new(RevealInProjectPanel {
|
||||
entry_id: Some(entry_id),
|
||||
})),
|
||||
cx.handler_for(&pane, move |pane, cx| {
|
||||
pane.project.update(cx, |_, cx| {
|
||||
cx.emit(project::Event::RevealInProjectPanel(
|
||||
ProjectEntryId::from_proto(entry_id),
|
||||
))
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1760,35 +1721,16 @@ impl Pane {
|
||||
return;
|
||||
}
|
||||
|
||||
let rect = event.bounds.size;
|
||||
|
||||
let size = event.bounds.size.width.min(event.bounds.size.height)
|
||||
* WorkspaceSettings::get_global(cx).drop_target_size;
|
||||
|
||||
let relative_cursor = Point::new(
|
||||
event.event.position.x - event.bounds.left(),
|
||||
event.event.position.y - event.bounds.top(),
|
||||
);
|
||||
|
||||
let direction = if relative_cursor.x < size
|
||||
|| relative_cursor.x > rect.width - size
|
||||
|| relative_cursor.y < size
|
||||
|| relative_cursor.y > rect.height - size
|
||||
{
|
||||
[
|
||||
SplitDirection::Up,
|
||||
SplitDirection::Right,
|
||||
SplitDirection::Down,
|
||||
SplitDirection::Left,
|
||||
]
|
||||
.iter()
|
||||
.min_by_key(|side| match side {
|
||||
SplitDirection::Up => relative_cursor.y,
|
||||
SplitDirection::Right => rect.width - relative_cursor.x,
|
||||
SplitDirection::Down => rect.height - relative_cursor.y,
|
||||
SplitDirection::Left => relative_cursor.x,
|
||||
})
|
||||
.cloned()
|
||||
let edge_width = cx.rem_size() * 8;
|
||||
let cursor = event.event.position;
|
||||
let direction = if cursor.x < event.bounds.left() + edge_width {
|
||||
Some(SplitDirection::Left)
|
||||
} else if cursor.x > event.bounds.right() - edge_width {
|
||||
Some(SplitDirection::Right)
|
||||
} else if cursor.y < event.bounds.top() + edge_width {
|
||||
Some(SplitDirection::Up)
|
||||
} else if cursor.y > event.bounds.bottom() - edge_width {
|
||||
Some(SplitDirection::Down)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -2071,7 +2013,10 @@ impl Render for Pane {
|
||||
div()
|
||||
.invisible()
|
||||
.absolute()
|
||||
.bg(cx.theme().colors().drop_target_background)
|
||||
.bg(theme::color_alpha(
|
||||
cx.theme().colors().drop_target_background,
|
||||
0.75,
|
||||
))
|
||||
.group_drag_over::<DraggedTab>("", |style| style.visible())
|
||||
.group_drag_over::<ProjectEntryId>("", |style| style.visible())
|
||||
.group_drag_over::<ExternalPaths>("", |style| style.visible())
|
||||
@@ -2087,22 +2032,17 @@ impl Render for Pane {
|
||||
.on_drop(cx.listener(move |this, paths, cx| {
|
||||
this.handle_external_paths_drop(paths, cx)
|
||||
}))
|
||||
.map(|div| {
|
||||
let size = DefiniteLength::Fraction(0.5);
|
||||
match self.drag_split_direction {
|
||||
None => div.top_0().right_0().bottom_0().left_0(),
|
||||
Some(SplitDirection::Up) => {
|
||||
div.top_0().left_0().right_0().h(size)
|
||||
}
|
||||
Some(SplitDirection::Down) => {
|
||||
div.left_0().bottom_0().right_0().h(size)
|
||||
}
|
||||
Some(SplitDirection::Left) => {
|
||||
div.top_0().left_0().bottom_0().w(size)
|
||||
}
|
||||
Some(SplitDirection::Right) => {
|
||||
div.top_0().bottom_0().right_0().w(size)
|
||||
}
|
||||
.map(|div| match self.drag_split_direction {
|
||||
None => div.top_0().left_0().right_0().bottom_0(),
|
||||
Some(SplitDirection::Up) => div.top_0().left_0().right_0().h_32(),
|
||||
Some(SplitDirection::Down) => {
|
||||
div.left_0().bottom_0().right_0().h_32()
|
||||
}
|
||||
Some(SplitDirection::Left) => {
|
||||
div.top_0().left_0().bottom_0().w_32()
|
||||
}
|
||||
Some(SplitDirection::Right) => {
|
||||
div.top_0().bottom_0().right_0().w_32()
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -649,7 +649,7 @@ mod element {
|
||||
children: Vec<PaneAxisChildLayout>,
|
||||
}
|
||||
|
||||
pub struct PaneAxisChildLayout {
|
||||
struct PaneAxisChildLayout {
|
||||
bounds: Bounds<Pixels>,
|
||||
element: AnyElement,
|
||||
handle: Option<PaneAxisHandleLayout>,
|
||||
@@ -793,8 +793,7 @@ mod element {
|
||||
|
||||
impl Element for PaneAxisElement {
|
||||
type BeforeLayout = ();
|
||||
type AfterLayout = Vec<PaneAxisChildLayout>;
|
||||
type BeforePaint = PaneAxisLayout;
|
||||
type AfterLayout = PaneAxisLayout;
|
||||
|
||||
fn before_layout(
|
||||
&mut self,
|
||||
@@ -812,9 +811,18 @@ mod element {
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
_state: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
) -> PaneAxisLayout {
|
||||
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
|
||||
Some(self.basis.into()),
|
||||
|state, _cx| {
|
||||
let state = state
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| Rc::new(RefCell::new(None)));
|
||||
(state.clone(), Some(state))
|
||||
},
|
||||
);
|
||||
let flexes = self.flexes.lock().clone();
|
||||
let len = self.children.len();
|
||||
debug_assert!(flexes.len() == len);
|
||||
@@ -836,9 +844,13 @@ mod element {
|
||||
let mut origin = bounds.origin;
|
||||
let space_per_flex = bounds.size.along(self.axis) / total_flex;
|
||||
|
||||
let mut focus_target_bounds = None;
|
||||
let mut bounding_boxes = self.bounding_boxes.lock();
|
||||
bounding_boxes.clear();
|
||||
|
||||
let mut children = Vec::new();
|
||||
let mut layout = PaneAxisLayout {
|
||||
dragged_handle: dragged_handle.clone(),
|
||||
children: Vec::new(),
|
||||
};
|
||||
for (ix, mut child) in mem::take(&mut self.children).into_iter().enumerate() {
|
||||
let child_flex = active_pane_magnification
|
||||
.map(|magnification| {
|
||||
@@ -854,76 +866,25 @@ mod element {
|
||||
.size
|
||||
.apply_along(self.axis, |_| space_per_flex * child_flex)
|
||||
.map(|d| d.round());
|
||||
let child_bounds = Bounds::new(origin, child_size);
|
||||
|
||||
let child_measurement = child.layout(child_size.into(), cx);
|
||||
if let Some(child_focus_target_bounds) = child_measurement.focus_target_bounds {
|
||||
focus_target_bounds = Some(Bounds::new(
|
||||
child_bounds.origin + child_focus_target_bounds.origin,
|
||||
child_focus_target_bounds.size,
|
||||
));
|
||||
}
|
||||
let child_bounds = Bounds {
|
||||
origin,
|
||||
size: child_size,
|
||||
};
|
||||
bounding_boxes.push(Some(child_bounds));
|
||||
child.layout(origin, child_size.into(), cx);
|
||||
|
||||
origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
|
||||
children.push(PaneAxisChildLayout {
|
||||
layout.children.push(PaneAxisChildLayout {
|
||||
bounds: child_bounds,
|
||||
element: child,
|
||||
handle: None,
|
||||
})
|
||||
}
|
||||
|
||||
(focus_target_bounds, children)
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
_before_layout: &mut Self::BeforeLayout,
|
||||
children: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> PaneAxisLayout {
|
||||
let dragged_handle = cx.with_element_state::<Rc<RefCell<Option<usize>>>, _>(
|
||||
Some(self.basis.into()),
|
||||
|state, _cx| {
|
||||
let state = state
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| Rc::new(RefCell::new(None)));
|
||||
(state.clone(), Some(state))
|
||||
},
|
||||
);
|
||||
|
||||
let magnification_value = WorkspaceSettings::get(None, cx).active_pane_magnification;
|
||||
let active_pane_magnification = if magnification_value == 1. {
|
||||
None
|
||||
} else {
|
||||
Some(magnification_value)
|
||||
};
|
||||
|
||||
let mut origin = bounds.origin;
|
||||
|
||||
let mut bounding_boxes = self.bounding_boxes.lock();
|
||||
bounding_boxes.clear();
|
||||
|
||||
let mut layout = PaneAxisLayout {
|
||||
dragged_handle: dragged_handle.clone(),
|
||||
children: Vec::new(),
|
||||
};
|
||||
for child in children {
|
||||
let child_bounds = Bounds {
|
||||
origin,
|
||||
size: child.bounds.size,
|
||||
};
|
||||
bounding_boxes.push(Some(child_bounds));
|
||||
cx.with_absolute_element_offset(origin, |cx| child.element.before_paint(cx));
|
||||
origin =
|
||||
origin.apply_along(self.axis, |val| val + child_bounds.size.along(self.axis));
|
||||
child.bounds = child_bounds;
|
||||
}
|
||||
|
||||
if active_pane_magnification.is_none() {
|
||||
let mut child_layouts = layout.children.iter_mut().peekable();
|
||||
while let Some(child_layout) = child_layouts.next() {
|
||||
if child_layouts.peek().is_some() {
|
||||
for (ix, child_layout) in layout.children.iter_mut().enumerate() {
|
||||
if active_pane_magnification.is_none() {
|
||||
if ix < len - 1 {
|
||||
child_layout.handle =
|
||||
Some(Self::layout_handle(self.axis, child_layout.bounds, cx));
|
||||
}
|
||||
@@ -937,8 +898,7 @@ mod element {
|
||||
&mut self,
|
||||
bounds: gpui::Bounds<ui::prelude::Pixels>,
|
||||
_: &mut Self::BeforeLayout,
|
||||
_: &mut Self::AfterLayout,
|
||||
layout: &mut Self::BeforePaint,
|
||||
layout: &mut Self::AfterLayout,
|
||||
cx: &mut ui::prelude::ElementContext,
|
||||
) {
|
||||
for child in &mut layout.children {
|
||||
|
||||
@@ -112,7 +112,6 @@ actions!(
|
||||
workspace,
|
||||
[
|
||||
Open,
|
||||
OpenInTerminal,
|
||||
NewFile,
|
||||
NewWindow,
|
||||
CloseWindow,
|
||||
@@ -4007,9 +4006,12 @@ impl Render for Workspace {
|
||||
.border_color(colors.border)
|
||||
.child({
|
||||
let this = cx.view().clone();
|
||||
canvas(move |bounds, cx| this.update(cx, |this, _cx| this.bounds = bounds))
|
||||
.absolute()
|
||||
.size_full()
|
||||
canvas(
|
||||
move |bounds, cx| this.update(cx, |this, _cx| this.bounds = bounds),
|
||||
|_, _, _| {},
|
||||
)
|
||||
.absolute()
|
||||
.size_full()
|
||||
})
|
||||
.when(self.zoomed.is_none(), |this| {
|
||||
this.on_drag_move(cx.listener(
|
||||
@@ -4970,7 +4972,6 @@ struct DisconnectedOverlay;
|
||||
impl Element for DisconnectedOverlay {
|
||||
type BeforeLayout = AnyElement;
|
||||
type AfterLayout = ();
|
||||
type BeforePaint = ();
|
||||
|
||||
fn before_layout(&mut self, cx: &mut ElementContext) -> (LayoutId, Self::BeforeLayout) {
|
||||
let mut background = cx.theme().colors().elevated_surface_background;
|
||||
@@ -4994,31 +4995,20 @@ impl Element for DisconnectedOverlay {
|
||||
}
|
||||
|
||||
fn after_layout(
|
||||
&mut self,
|
||||
_bounds: Bounds<Pixels>,
|
||||
overlay: &mut Self::BeforeLayout,
|
||||
cx: &mut ElementContext,
|
||||
) -> (Option<Bounds<Pixels>>, Self::AfterLayout) {
|
||||
(overlay.after_layout(cx), ())
|
||||
}
|
||||
|
||||
fn before_paint(
|
||||
&mut self,
|
||||
bounds: Bounds<Pixels>,
|
||||
overlay: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
cx.insert_hitbox(bounds, true);
|
||||
overlay.before_paint(cx);
|
||||
overlay.after_layout(cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_: Bounds<Pixels>,
|
||||
overlay: &mut Self::BeforeLayout,
|
||||
_after_layout: &mut Self::AfterLayout,
|
||||
_: &mut Self::BeforePaint,
|
||||
_: &mut Self::AfterLayout,
|
||||
cx: &mut ElementContext,
|
||||
) {
|
||||
overlay.paint(cx)
|
||||
|
||||
@@ -12,7 +12,6 @@ pub struct WorkspaceSettings {
|
||||
pub show_call_status_icon: bool,
|
||||
pub autosave: AutosaveSetting,
|
||||
pub restore_on_startup: RestoreOnStartupBehaviour,
|
||||
pub drop_target_size: f32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
@@ -51,11 +50,6 @@ pub struct WorkspaceSettingsContent {
|
||||
/// Values: none, last_workspace
|
||||
/// Default: last_workspace
|
||||
pub restore_on_startup: Option<RestoreOnStartupBehaviour>,
|
||||
/// The size of the workspace split drop targets on the outer edges.
|
||||
/// Given as a fraction that will be multiplied by the smaller dimension of the workspace.
|
||||
///
|
||||
/// Default: `0.2` (20% of the smaller dimension of the workspace)
|
||||
pub drop_target_size: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.133.0"
|
||||
version = "0.132.0"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -1294,7 +1294,6 @@ Run the `theme selector: toggle` action in the command palette to see a current
|
||||
|
||||
```json
|
||||
"project_panel": {
|
||||
"button": true,
|
||||
"dock": "left",
|
||||
"git_status": true,
|
||||
"default_width": "N/A - width in pixels"
|
||||
|
||||
@@ -25,7 +25,7 @@ git submodule update --init --recursive
|
||||
rustup target add wasm32-wasi
|
||||
```
|
||||
|
||||
- Install [Visual Studio](https://visualstudio.microsoft.com/downloads/) with optional component `MSVC v*** - VS YYYY C++ x64/x86 build tools` and install Windows 11 or 10 SDK depending on your system
|
||||
- Install [Visual Studio](https://visualstudio.microsoft.com/downloads/) with optional component `MSVC v*** - VS YYYY C++ x64/x86 build tools`.
|
||||
|
||||
> [!NOTE]
|
||||
> `v***` is your VS version and `YYYY` is year when your VS was released.
|
||||
|
||||
@@ -17,31 +17,25 @@ You will need write access to the Zed repository to do this:
|
||||
- Run `./script/bump-zed-minor-versions` and push the tags
|
||||
and branches as instructed.
|
||||
- Wait for the builds to appear at https://github.com/zed-industries/zed/releases (typically takes around 30 minutes)
|
||||
- While you're waiting:
|
||||
- Start creating the new release notes for preview. You can start with the output of `./script/get-preview-channel-changes`.
|
||||
- Start drafting the release tweets.
|
||||
- Once the builds are ready:
|
||||
- Copy the release notes from the previous Preview release(s) to the current Stable release.
|
||||
- Download the artifacts for each release and test that you can run them locally.
|
||||
- Publish the releases on GitHub.
|
||||
- Tweet the tweets (Credentials are in 1password).
|
||||
- Copy the release notes from the previous Preview release(s) to the current Stable release.
|
||||
- Write new release notes for Preview. `/script/get-preview-channel-changes` can help with this, but you'll need to edit and format the output to make it good.
|
||||
- Download the artifacts for each release and test that you can run them locally.
|
||||
- Publish the releases.
|
||||
|
||||
## Patch release process
|
||||
|
||||
If your PR fixes a panic or a crash, you should cherry-pick it to the current stable and preview branches. If your PR fixes a regression in recently released code, you should cherry-pick it to preview.
|
||||
If your PR fixes a panic or a crash, you should cherry-pick it to the current stable and preview branches. If your PR fixes a regression in recently released code, you should cherry-pick it to the appropriate branch.
|
||||
|
||||
You will need write access to the Zed repository to do this:
|
||||
|
||||
- Send a PR containing your change to `main` as normal.
|
||||
- Leave a comment on the PR `/cherry-pick v0.XXX.x`. Once your PR is merged, the Github bot will send a PR to the branch.
|
||||
- In case of a merge conflict, you will have to cherry-pick manually and push the change to the `v0.XXX.x` branch.
|
||||
- After the commits are cherry-picked onto the branch, run `./script/trigger-release {preview|stable}`. This will bump the version numbers, create a new release tag, and kick off a release build.
|
||||
- Cherry pick them onto the correct branch. You can either do this manually, or leave a comment of the form `/cherry-pick v0.XXX.x` on the PR, and the GitHub bot should do it for you.
|
||||
- Run `./script/trigger-release {preview|stable}`
|
||||
- Wait for the builds to appear at https://github.com/zed-industries/zed/releases (typically takes around 30 minutes)
|
||||
- Proof-read and edit the release notes as needed.
|
||||
- Add release notes using the `Release notes:` section of each cherry-picked PR.
|
||||
- Download the artifacts for each release and test that you can run them locally.
|
||||
- Publish the release.
|
||||
|
||||
## Nightly release process
|
||||
|
||||
In addition to the public releases, we also have a nightly build that we encourage employees to use.
|
||||
Nightly is released by cron once a day, and can be shipped as often as you'd like. There are no release notes or announcements, so you can just merge your changes to main and run `./script/trigger-release nightly`.
|
||||
- Merge your changes to main
|
||||
- Run `./script/trigger-release {nightly}`
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
[package]
|
||||
name = "zed_glsl"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "Apache-2.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/glsl.rs"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
zed_extension_api = "0.0.6"
|
||||
@@ -1,15 +1,11 @@
|
||||
id = "glsl"
|
||||
name = "GLSL"
|
||||
description = "GLSL support."
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
schema_version = 1
|
||||
authors = ["Mikayla Maki <mikayla@zed.dev>"]
|
||||
repository = "https://github.com/zed-industries/zed"
|
||||
|
||||
[language_servers.glsl_analyzer]
|
||||
name = "GLSL Analyzer LSP"
|
||||
language = "GLSL"
|
||||
|
||||
[grammars.glsl]
|
||||
repository = "https://github.com/theHamsta/tree-sitter-glsl"
|
||||
commit = "31064ce53385150f894a6c72d61b94076adf640a"
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
name = "GLSL"
|
||||
grammar = "glsl"
|
||||
path_suffixes = [
|
||||
# Traditional rasterization pipeline shaders
|
||||
"vert", "frag", "tesc", "tese", "geom",
|
||||
# Compute shaders
|
||||
"comp",
|
||||
# Ray tracing pipeline shaders
|
||||
"rgen", "rint", "rahit", "rchit", "rmiss", "rcall",
|
||||
# Other
|
||||
"glsl"
|
||||
]
|
||||
path_suffixes = ["vert", "frag", "tesc", "tese", "geom", "comp"]
|
||||
line_comments = ["// "]
|
||||
block_comment = ["/* ", " */"]
|
||||
brackets = [
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
use std::fs;
|
||||
use zed::settings::LspSettings;
|
||||
use zed_extension_api::{self as zed, serde_json, LanguageServerId, Result};
|
||||
|
||||
struct GlslExtension {
|
||||
cached_binary_path: Option<String>,
|
||||
}
|
||||
|
||||
impl GlslExtension {
|
||||
fn language_server_binary_path(
|
||||
&mut self,
|
||||
language_server_id: &LanguageServerId,
|
||||
worktree: &zed::Worktree,
|
||||
) -> Result<String> {
|
||||
if let Some(path) = worktree.which("glsl_analyzer") {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
if let Some(path) = &self.cached_binary_path {
|
||||
if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
|
||||
return Ok(path.clone());
|
||||
}
|
||||
}
|
||||
|
||||
zed::set_language_server_installation_status(
|
||||
&language_server_id,
|
||||
&zed::LanguageServerInstallationStatus::CheckingForUpdate,
|
||||
);
|
||||
let release = zed::latest_github_release(
|
||||
"nolanderc/glsl_analyzer",
|
||||
zed::GithubReleaseOptions {
|
||||
require_assets: true,
|
||||
pre_release: false,
|
||||
},
|
||||
)?;
|
||||
|
||||
let (platform, arch) = zed::current_platform();
|
||||
let asset_name = format!(
|
||||
"{arch}-{os}.zip",
|
||||
arch = match arch {
|
||||
zed::Architecture::Aarch64 => "aarch64",
|
||||
zed::Architecture::X86 => "x86",
|
||||
zed::Architecture::X8664 => "x86_64",
|
||||
},
|
||||
os = match platform {
|
||||
zed::Os::Mac => "macos",
|
||||
zed::Os::Linux => "linux-musl",
|
||||
zed::Os::Windows => "windows",
|
||||
}
|
||||
);
|
||||
|
||||
let asset = release
|
||||
.assets
|
||||
.iter()
|
||||
.find(|asset| asset.name == asset_name)
|
||||
.ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
|
||||
|
||||
let version_dir = format!("glsl_analyzer-{}", release.version);
|
||||
fs::create_dir_all(&version_dir)
|
||||
.map_err(|err| format!("failed to create directory '{version_dir}': {err}"))?;
|
||||
let binary_path = format!("{version_dir}/bin/glsl_analyzer");
|
||||
|
||||
if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
|
||||
zed::set_language_server_installation_status(
|
||||
&language_server_id,
|
||||
&zed::LanguageServerInstallationStatus::Downloading,
|
||||
);
|
||||
|
||||
zed::download_file(
|
||||
&asset.download_url,
|
||||
&version_dir,
|
||||
match platform {
|
||||
zed::Os::Mac | zed::Os::Linux => zed::DownloadedFileType::Zip,
|
||||
zed::Os::Windows => zed::DownloadedFileType::Zip,
|
||||
},
|
||||
)
|
||||
.map_err(|e| format!("failed to download file: {e}"))?;
|
||||
|
||||
zed::make_file_executable(&binary_path)?;
|
||||
|
||||
let entries =
|
||||
fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
|
||||
if entry.file_name().to_str() != Some(&version_dir) {
|
||||
fs::remove_dir_all(&entry.path()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.cached_binary_path = Some(binary_path.clone());
|
||||
Ok(binary_path)
|
||||
}
|
||||
}
|
||||
|
||||
impl zed::Extension for GlslExtension {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
cached_binary_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn language_server_command(
|
||||
&mut self,
|
||||
language_server_id: &zed::LanguageServerId,
|
||||
worktree: &zed::Worktree,
|
||||
) -> Result<zed::Command> {
|
||||
Ok(zed::Command {
|
||||
command: self.language_server_binary_path(language_server_id, worktree)?,
|
||||
args: vec![],
|
||||
env: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn language_server_workspace_configuration(
|
||||
&mut self,
|
||||
_language_server_id: &zed::LanguageServerId,
|
||||
worktree: &zed::Worktree,
|
||||
) -> Result<Option<serde_json::Value>> {
|
||||
let settings = LspSettings::for_worktree("glsl_analyzer", worktree)
|
||||
.ok()
|
||||
.and_then(|lsp_settings| lsp_settings.settings.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Some(serde_json::json!({
|
||||
"glsl_analyzer": settings
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
zed::register_extension!(GlslExtension);
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "zed_prisma"
|
||||
version = "0.0.2"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "prisma"
|
||||
name = "Prisma"
|
||||
description = "Prisma support."
|
||||
version = "0.0.2"
|
||||
version = "0.0.1"
|
||||
schema_version = 1
|
||||
authors = ["Matthew Gramigna <matthewgramigna@gmail.com>"]
|
||||
repository = "https://github.com/zed-industries/zed"
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
#!/usr/bin/env node --redirect-warnings=/dev/null
|
||||
|
||||
const { execFileSync } = require("child_process");
|
||||
|
||||
main();
|
||||
|
||||
async function main() {
|
||||
let version = process.argv[2];
|
||||
let channel = process.argv[3];
|
||||
let parts = version.split(".");
|
||||
|
||||
if (
|
||||
process.argv.length != 4 ||
|
||||
parts.length != 3 ||
|
||||
parts.find((part) => isNaN(part)) != null ||
|
||||
(channel != "stable" && channel != "preview")
|
||||
) {
|
||||
console.log("Usage: draft-release-notes <version> {stable|preview}");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let priorVersion = [parts[0], parts[1], parts[2] - 1].join(".");
|
||||
let suffix = "";
|
||||
|
||||
if (channel == "preview") {
|
||||
suffix = "-pre";
|
||||
if (parts[2] == 0) {
|
||||
priorVersion = [parts[0], parts[1] - 1, 0].join(".");
|
||||
}
|
||||
} else if (!tagExists("v${priorVersion}")) {
|
||||
console.log("Copy the release notes from preview.");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let [tag, priorTag] = [`v${version}${suffix}`, `v${priorVersion}${suffix}`];
|
||||
|
||||
const newCommits = getCommits(priorTag, tag);
|
||||
|
||||
let releaseNotes = [];
|
||||
let missing = [];
|
||||
let skipped = [];
|
||||
|
||||
for (const commit of newCommits) {
|
||||
let link = "https://github.com/zed-industries/zed/pull/" + commit.pr;
|
||||
let notes = commit.releaseNotes;
|
||||
if (commit.pr == "") {
|
||||
link = "https://github.com/zed-industries/zed/commits/" + commit.hash;
|
||||
} else if (!notes.includes("zed-industries/zed/issues")) {
|
||||
notes = notes + " ([#" + commit.pr + "](" + link + "))";
|
||||
}
|
||||
|
||||
if (commit.releaseNotes == "") {
|
||||
missing.push("- MISSING " + commit.firstLine + " " + link);
|
||||
} else if (commit.releaseNotes.startsWith("- N/A")) {
|
||||
skipped.push("- N/A " + commit.firstLine + " " + link);
|
||||
} else {
|
||||
releaseNotes.push(notes);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(releaseNotes.join("\n") + "\n");
|
||||
console.log("<!-- ");
|
||||
console.log(missing.join("\n"));
|
||||
console.log(skipped.join("\n"));
|
||||
console.log("-->");
|
||||
}
|
||||
|
||||
function getCommits(oldTag, newTag) {
|
||||
const pullRequestNumbers = execFileSync(
|
||||
"git",
|
||||
["log", `${oldTag}..${newTag}`, "--format=DIVIDER\n%H|||%B"],
|
||||
{ encoding: "utf8" },
|
||||
)
|
||||
.replace(/\r\n/g, "\n")
|
||||
.split("DIVIDER\n")
|
||||
.filter((commit) => commit.length > 0)
|
||||
.map((commit) => {
|
||||
let [hash, firstLine] = commit.split("\n")[0].split("|||");
|
||||
let cherryPick = firstLine.match(/\(cherry-pick #([0-9]+)\)/)?.[1] || "";
|
||||
let pr = firstLine.match(/\(#(\d+)\)$/)?.[1] || "";
|
||||
let releaseNotes = (commit.split(/Release notes:.*\n/i)[1] || "")
|
||||
.split("\n\n")[0]
|
||||
.trim()
|
||||
.replace(/\n(?![\n-])/g, " ");
|
||||
|
||||
if (releaseNotes.includes("<public_issue_number_if_exists>")) {
|
||||
releaseNotes = "";
|
||||
}
|
||||
|
||||
return {
|
||||
hash,
|
||||
pr,
|
||||
cherryPick,
|
||||
releaseNotes,
|
||||
firstLine,
|
||||
};
|
||||
});
|
||||
|
||||
return pullRequestNumbers;
|
||||
}
|
||||
|
||||
function tagExists(tag) {
|
||||
try {
|
||||
execFileSync("git", ["rev-parse", "--verify", tag]);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
1
script/script.py
Normal file
1
script/script.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user