The function returns the previous focus handle, which may be null if
there is no previous focus. Unfortunately that also overlaps with the
error return value, so winapi will hand us a error 0 back in those cases
which we log ...
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Continuing of #44334
I removed disabling of vsync which was causing jitter on some external
displays
cc: @maxbrunsfeld @Anthony-Eid
Release Notes:
- Mark metal layers opaque for non-transparent windows to allow
direct-to-display when supported
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Closes https://github.com/zed-industries/zed/issues/43208
This PR essentially unblocks the editable number field. The function
that shapes editor lines was hard-coding text alignment to the left,
meaning that whatever different alignment we'd pass through
`EditorStyles`would be ignored. To solve this, I just added a text align
and align width fields to the line paint function and updated all call
sites keeping the default configuration. Had to also add an
`alignment_offset()` helper to make sure the cursor positioning, the
selection background element, and the click-to-focus functionality were
kept in-sync with the non-left aligned editor.
Then... the big star of the show here is being able to add the `mode`
method to the number field, which uses `TextAlign::Center`, thus making
it work as we designed it to work.
https://github.com/user-attachments/assets/3539c976-d7bf-4d94-8188-a14328f94fbf
Next up, is turning the number filed to edit mode where applicable.
Release Notes:
- Fixed a bug where different text alignment configurations (i.e.,
center and right-aligned) wouldn't take effect in editors.
Currently on x11, gpui PopUp windows only rely on the "notification"
type in order to indicate that they should spawn as floating window.
Several window managers (leftwm in my case, but it also seems to be the
case for dwm and ratpoison) do not this property into account thus not
spawning them as float. On the other hand, using Floating instead of
PopUp do make those windows spawn as floating, as these window manager
do take into account the (older) "dialog" type.
The [freedekstop
documentation](https://specifications.freedesktop.org/wm/1.5/ar01s05.html#id-1.6.7)
does seem to suggest that these windows should also have the override
redirect property :
> This property is typically used on override-redirect windows.
Note that this also disables pretty much all interactions with the
window manager (such as moving the window, resizing etc...)
Release Notes:
- Fix popup windows not spawning floating sometime on x11
This fixes an X11 scrolling issue where Zed may jump by a large amount
due to the scroll valuator state not being reset when the window loses
focus. If you Alt-Tab away from Zed, scroll in another application, then
return, the first scroll event in Zed applies the entire accumulated
delta instead of a single step.
The missing FocusOut reset was originally identified in issue #34901.
Resetting scroll positions on FocusOut matches the behavior already
implemented in the XinputLeave handler and prevents this jump.
Closes#34901Closes#40538
Release Notes:
- Fixed an X11 issue where Alt-Tabbing to another application,
scrolling, and returning to Zed could cause the next scroll event to
jump by a large amount.
I've just enriched the existing tab_stop.rs example for GPUI with a
demonstration of tab_group. I don't think tab groups existed when the
original example was written.
(I didn't understand the behaviour for tab_group from the doccomments
and the example was missing, so I think this is a productive PR)
Release Notes:
- N/A
Reverts zed-industries/zed#44334
From my testing, this PR introduced screen tearing, or some kind of
strange visual artifact, when scrolling at medium speed on a large
display.
Release notes:
- N/A
Closes#17467
Release Notes:
- On macOS, buffer search now syncs with the system find pasteboard,
allowing <kbd>⌘E</kbd> and <kbd>⌘G</kbd> to work seamlessly across Zed
and other apps.
It's been a little that we've noticed some flickering and other weird
resizing behavior with text truncation in Zed:
https://github.com/user-attachments/assets/4d5691a3-cd3d-45e0-8b96-74a4e0e273d2https://github.com/user-attachments/assets/d1d0e587-7676-4da0-8818-f4e50f0e294e
Initially, we suspected this could be due to how we calculate the length
of a line to insert truncation, which is based first on the length of
each individual character, and then second goes through a pass
calculating the line length as a whole. This could cause mismatch and
culminate in our bug.
However, even though that felt like a reasonable suspicion, I realized
something rather simple at some point: the `truncate` and
`truncate_start` methods in the `Label` didn't use `whitespace_nowrap`.
If you take Tailwind as an example, their `truncate` utility class takes
`overflow: hidden; text-overflow: ellipsis; white-space: nowrap;`. This
pointed out to a potential bug with `whitespace_nowrap` where that was
blocking truncation entirely, even though that's technically part of
what's necessary to truncate as you don't want text that will be
truncated to wrap.
Ultimately, what was happening was that the text element was caching its
layout based on its `wrap_width` but not considering its
`truncate_width`. The truncate width is essentially the new definitive
width of the text based on the available space, which was never being
computed. So the fix here was to add `truncate_width.is_none()` to the
cache validation check, so that it only uses the cached text element
size _if the truncation width is untouched_. But if that changes, we
need to account for the new width. Then, in the Label component, we
added `min_w_0` to allow the label div to shrink below its original
size, and finally, we added `whitespace_nowrap()` as the cache check
fundamentally fixed that method's problem.
In a future PR, we can basically remove the `single_line()` label method
because: 1) whenever you want a single label, you most likely want it to
truncate, and 2) most instances of `truncate` are already followed by
`single_line` in Zed today, so we can cut that part.
Result is no flickering with truncated labels!
https://github.com/user-attachments/assets/ae17cbde-0de7-42ca-98a4-22fcb452016b
Release Notes:
- Fixed a bug in GPUI where truncated text would flicker as you resized
the container in which the text was in.
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
Fixes#29073
This PR reduces unnecessary GPU usage by being more selective about when
we present frames to prevent display underclocking (VRR optimization).
## Problem
Previously, we would keep presenting frames for 1 second after *any*
input event, regardless of whether it triggered a re-render. This caused
unnecessary GPU work when the user was idle or during low-frequency
interactions.
## Solution
1. **Only track input that triggers re-renders**: We now only record
input timestamps when the input actually causes the window to become
dirty, rather than on every input event.
2. **Rate-based activation**: The VRR optimization now only activates
when input arrives at a high rate (≥ 60fps over the last 100ms). This
means casual mouse movements or occasional keystrokes won't trigger
continuous frame presentation.
3. **Sustained optimization**: Once high-rate input is detected (e.g.,
during scrolling or dragging), we sustain frame presentation for 1
second to prevent display underclocking, even if input briefly pauses.
## Implementation
Added `InputRateTracker` which:
- Tracks input timestamps in a 100ms sliding window
- Activates when the window contains ≥ 6 events (60fps × 0.1s)
- Extends a `sustain_until` timestamp by 1 second each time high rate is
detected
Release Notes:
- Reduced GPU usage when idle by only presenting frames during bursts of
high-frequency input.
This fixes a number of issues where zed depends on the order of polling which changed when switching scheduler. We have adjusted the algorithm so it matches the previous order while keeping the prioritization feature.
Release Notes:
- N/A
This PR is an additive change introducing the `truncate_start` method to
labels, which gives us the ability to add an ellipsis at the beginning
of the text as opposed to the regular `truncate`. This will be generally
used for truncating file paths, where the end is typically more relevant
than the beginning, but given it's a general method, there's the
possibility to be used anywhere else, too.
<img width="500" height="690" alt="Screenshot 2025-12-17 at 12 35@2x"
src="https://github.com/user-attachments/assets/f853f5a3-60b3-4380-a11c-bb47868a4470"
/>
Release Notes:
- N/A
---------
Co-authored-by: Lukas Wirth <lukas@zed.dev>
Closes#43355
Fixes the issue were code actions with long labels would get cut off
without being able to see the full description. We now properly truncate
those labels with an ellipsis and show the full description in an aside.
Release Notes:
- Added ellipsis to truncated code actions and an aside showing the full
action description.
When profiling Zed with Instruments, a warning appears indicating that
surfaces cannot be pushed directly to the display as they are
non-opaque. This happens because the metal layer is currently marked as
non-opaque by default, even though the window itself is not transparent.
<img width="590" height="55" alt="image"
src="https://github.com/user-attachments/assets/2647733e-c75b-4aec-aa19-e8b2ffd6194b"
/>
Metal on macOS can bypass compositing and present frames directly to the
display when several conditions are met. One of those conditions is that
the backing layer must be declared opaque. Apple’s documentation notes
that marking layers as opaque allows the system to avoid unnecessary
compositing work, reducing GPU load and improving frame pacing
Ref:
https://developer.apple.com/documentation/metal/managing-your-game-window-for-metal-in-macos
This PR updates the Metal renderer to mark the layer as opaque whenever
the window does not use transparency. This makes Zed eligible for
macOS’s direct-to-display optimization in scenarios where the system can
apply it.
Release Notes:
- gpui: Mark metal layers opaque for non-transparent windows to allow
direct-to-display when supported
---------
Signed-off-by: Marco Mihai Condrache <52580954+marcocondrache@users.noreply.github.com>
Closes #ISSUE
Removes a few eager container clones and iterations.
Added a todo to `get_prev_tab_group_window` and
`get_next_tab_group_window`. They seem to use `HashMap::keys()` for
choosing the previous tab group, however `.keys()` returns an arbitrary
order, so I'm not sure if previous actually means anything here. Conrad
seems to have worked on this part previously, maybe he has some
insights. That can possibly be a follow-up PR, but I'd be willing to
work on it here as well since the other changes are so simple.
Release Notes:
- N/A
Closes #ISSUE
A [modal dialog](https://en.wikipedia.org/wiki/Modal_window) window is a
window that demands the user's immediate attention and blocks
interaction with other parts of the application until it's closed.
- On Windows this is done by disabling the parent window when the dialog
window is created and re-enabling the parent window when closed.
- On Wayland this is done using the
[`XdgDialog`](https://wayland.app/protocols/xdg-dialog-v1) protocol,
which hints to the compositor that the dialog should be modal. While
compositors like GNOME and KDE block parent interaction automatically,
the XDG specification does not guarantee this behavior, compositors may
deliver events to the parent window unfiltered. Since the specification
explicitly requires clients to implement event filtering logic
themselves, this PR implements client-side blocking in GPUI to ensure
consistent modal behavior across all Wayland compositors, including
those like Hyprland that don't block parent interaction.
- On X11 this is done by enabling the application window property
[`_NET_WM_STATE_MODAL`](https://specifications.freedesktop.org/wm/latest/ar01s05.html#id-1.6.8)
state.
I'm unable to implement this on MacOS as I lack the experience and the
hardware to test it. If anyone is interested on implementing this let me
know.
|Window|Linux (wayland)| Linux (x11) |MacOS|
|-|-|-|-|
|<video
src="https://github.com/user-attachments/assets/bfd0733a-445d-4b63-ac6b-ebe098a7dc74"></video>|<video
src="https://github.com/user-attachments/assets/024cd6ec-ff81-4250-a5be-5d207a023f8c"></video>|
N/A | <video
src="https://github.com/user-attachments/assets/656e60a5-26b2-4ee2-8368-1fbbe872453c"></video>|
TODO:
- [x] Block parent interaction client-side on X11
Release Notes:
- Added modal dialog window kind on GPUI
---------
Co-authored-by: Jason Lee <huacnlee@gmail.com>
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
The below memory leaks were caused by failing to release reference
counted resources. I confirmed using instruments that my changes stopped
the leaks from occurring.
- System prompts
- Screen capturing
- loading font families
There were also two memory leaks I found from some of our dependencies
that I made PRs to fix
- https://github.com/RustAudio/coreaudio-rs/pull/147
- https://github.com/servo/core-foundation-rs/pull/746
Release Notes:
- N/A
Closes #ISSUE
Problem:
- The status bar’s pending keystroke indicator (shown next to --NORMAL--
in Vim mode) didn’t clear when focus moved to another context, e.g.
hitting g in the editor then clicking the Git panel. The keymap state
correctly canceled the prefix, but observers that render the indicator
never received a “pending input changed” notification, so the UI kept
showing stale prefixes until a new keystroke occurred.
Fix:
- The change introduces a `pending_input_changed_queued` flag and a new
helper `notify_pending_input_if_needed` which will flushes the queued
notification as soon as we have an App context. The
`pending_input_changed` now resets the flag after notifying subscribers.
Before:
https://github.com/user-attachments/assets/7bec4c34-acbf-42bd-b0d1-88df5ff099aa
After:
https://github.com/user-attachments/assets/2264dc93-3405-4d63-ad8f-50ada6733ae7
Release Notes:
- Fixed: pending keybinding prefixes on the status bar now clear
immediately when focus moves to another panel or UI context.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Release Notes:
- Improved display map rendering performance with many lines in the the multi-buffer.
---------
Co-authored-by: cameron <cameron.studdstreet@gmail.com>
Co-authored-by: Cole Miller <cole@zed.dev>
Closes #ISSUE
Remove some intermediate allocations when reconstructing text or wrapped
text from a `TextLayout`. Currently creates a intermediate `Vec<String>`
which gets joined, when you could join an `impl Iterator<Item = &str>`
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Closes#40678
The python file below simulates the macros at various timings and can be
run by running:
1. `sudo python3 -m pip install evdev --break-system-packages`
2. `sudo python3 zed_shift_brace_replayer.py`
Checked timings for hold=0.1, =0.01 and =0.001 with the latter two no
longer causing incorrect inputs.
[zed_shift_brace_replayer.py](https://github.com/user-attachments/files/23560570/zed_shift_brace_replayer.py)
Release Notes:
- linux: fixed a race condition where the macros containing modifier +
key would sometimes be processed without the modifier
Required for https://github.com/zed-industries/zed/pull/44712
We started using `grid` for Markdown tables instead of flex. This
resulted in tables having a width of 0 inside popovers, since popovers
are laid out using `AvailableSpace::MinContent`.
One way to fix this is to lay out popovers using `MaxContent` instead.
But that would affect all Markdown rendered in popovers and could change
how popovers look, or regress things.
The other option is to fix it where the problem actually is:
`repeat(count, vec![minmax(length(0.0), fr(1.0))])`. Since the minimum
width here is `0`, laying things out with `MinContent` causes the
Markdown table to shrink completely. What we want instead is for the
minimum width to be the min-content size, but only for Markdown rendered
inside popovers.
This PR does exactly that, without interfering with the `grid_cols` API,
which intentionally follows a TailwindCSS-like convention. See
https://github.com/zed-industries/zed/pull/44368 for context.
Release Notes:
- N/A
Closes#4644
Release Notes:
- Adds `MousePressureEvent`, an event that is sent anytime the touchpad
pressure changes, into `gpui`. MacOS only.
- Triggers go-to-defintion on force clicks in the editor.
This is my first contribution, let me know if I've missed something
here.
---------
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Antonio Scandurra <me@as-cii.com>
## Motivating problem
The gpui API currently has this counter intuitive behaviour
```rust
div()
.id("hallo")
.cursor_pointer()
.text_color(white())
.font_weight(FontWeight::SEMIBOLD)
.text_size(px(20.0))
.child("hallo")
.active(|this| this.text_color(red()))
```
By changing the text_color when the div is active, the current behaviour
is to overwrite all of the text styling rather than do a proper
refinement of the existing text styling leading to this odd result:
The button being active inadvertently changes the font size.
https://github.com/user-attachments/assets/1ff51169-0d76-4ee5-bbb0-004eb9ffdf2c
## Solution
Previously refining a Style would not recursively refine the TextStyle
inside of it, leading to this behaviour:
```rust
let mut style = Style::default();
style.refine(&StyleRefinement::default().text_size(px(20.0)));
style.refine(&StyleRefinement::default().font_weight(FontWeight::SEMIBOLD));
assert!(style.text_style().unwrap().font_size.is_none());
//assertion passes
```
(As best as I can tell) Style deliberately has `pub text:
TextStyleRefinement` storing the `TextStyleRefinement` rather than the
absolute `TextStyle` so that these refinements can be elsewhere used in
cascading text styles down to element's children. But a consequence of
that is that the refine macro was not properly recursively refining the
`text` field as it ought to.
I've modified the refine macro so that the `#[refineable]` attribute
works with `TextStyleRefinement` as well as the usual `TextStyle`.
(Perhaps a little bit haphazardly by simply checking whether the name
ends in Refinement - there may be a better solution there).
This PR resolves the motivating problem and triggers the assertion in
the above code as you'd expect. I've compiled zed under these changes
and all seems to be in order there.
Release Notes:
- N/A
---------
Co-authored-by: Antonio Scandurra <me@as-cii.com>
This adds implementations of `std::iter::Sum` for `Pixels`, allowing the
use of `.sum()` on iterators of `Pixels` values.
### Changes
- Implement `Sum<Pixels>` for `Pixels` (owned values)
- Implement `Sum<&Pixels>` for `Pixels` (references)
This enables ergonomic patterns like:
```rust
let total: Pixels = pixel_values.iter().sum();
```
Improves the scheduler by allowing tasks to have a set priority which
will significantly improve responsiveness.
Release notes:
- N/A
---------
Co-authored-by: Yara <git@yara.blue>
Co-authored-by: dvdsk <noreply@davidsk.dev>
- Fix missing font features in
`git_ui::blame_ui::GitBlameRenderer.render_blame_entry`
- Fix missing buffer font features in
`markdown_preview::markdown_renderer`
- Update the way that the markdown style is built for hover popovers so
that, for code blocks, the buffer font features are used.
- Introduce `gpui::Styled.font_features` to allow callers to also set
the font's features, similar to how `gpui::Styled.font_family` already
exists.
Relates to #44209
Release Notes:
- Fixed wrong font features in Blame UI, Hover Popover and Markdown
Preview
Closes https://github.com/zed-industries/zed/issues/39056
Leverages a new `await_on_background` API that spawns the future on the
background but blocks the current task, allowing to borrow from the
surrounding scope.
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Improves the scheduler by allowing tasks to have a set priority which
will significantly improve responsiveness.
Release notes:
- N/A
---------
Co-authored-by: Yara <git@yara.blue>