Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Clip branch picker labels from left
Co-Authored-By: Oz <[email protected]>
  • Loading branch information
moirahuang and oz-agent committed May 13, 2026
commit 653c4345bdf8d58b86a9c1e16b7f37b689088480
18 changes: 11 additions & 7 deletions app/src/tab_configs/branch_picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::path::PathBuf;

use warpui::{
elements::ChildView, ui_components::components::UiComponentStyles, AppContext, Element, Entity,
TypedActionView, View, ViewContext, ViewHandle,
text_layout::ClipConfig, TypedActionView, View, ViewContext, ViewHandle,
};

use crate::{
Expand All @@ -18,6 +18,12 @@ const DEFAULT_DROPDOWN_WIDTH: f32 = 380.;
/// Placeholder text shown in the dropdown top bar while branches are loading.
const LOADING_PLACEHOLDER: &str = "Fetching branches\u{2026}";

fn branch_dropdown_item(name: String) -> DropdownItem<String> {
DropdownItem::new(name.clone(), name.clone())
.with_clip_config(ClipConfig::start())
.with_tooltip(name)
}

/// A filterable dropdown that lists local git branches for the given repo path.
///
/// Created with an optional `cwd` — if `None`, the picker starts with the
Expand Down Expand Up @@ -69,6 +75,7 @@ impl BranchPicker {
let dropdown = ctx.add_typed_action_view(|ctx| {
let mut dropdown = FilterableDropdown::new(ctx);
dropdown.set_top_bar_max_width(width);
dropdown.set_top_bar_text_clip_config(ClipConfig::start());
dropdown.set_menu_width(width, ctx);
if let Some(bg) = bg {
dropdown.set_style(UiComponentStyles {
Expand All @@ -93,10 +100,7 @@ impl BranchPicker {
if let Some(ref default) = default_value {
let default = default.clone();
picker.dropdown.update(ctx, |dropdown, ctx| {
dropdown.set_items(
vec![DropdownItem::new(default.clone(), default.clone())],
ctx,
);
dropdown.set_items(vec![branch_dropdown_item(default.clone())], ctx);
dropdown.set_selected_by_name(default.as_str(), ctx);
});
}
Expand Down Expand Up @@ -192,14 +196,14 @@ impl BranchPicker {

// Main branches first, then the rest in recency order.
let mut items: Vec<DropdownItem<String>> = sort_branches_main_first(&branches)
.map(|(name, _)| DropdownItem::new(name.clone(), name.clone()))
.map(|(name, _)| branch_dropdown_item(name.clone()))
.collect();

// Add the default as the first item if it isn't already in the list
// (e.g. the user typed a branch name that doesn't exist locally yet).
if let Some(ref default) = me.default_value {
if !branches.iter().any(|(name, _)| name == default) {
items.insert(0, DropdownItem::new(default.clone(), default.clone()));
items.insert(0, branch_dropdown_item(default.clone()));
}
}

Expand Down
34 changes: 22 additions & 12 deletions app/src/view_components/filterable_dropdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use warpui::{
PositionedElementOffsetBounds, Radius, SavePosition, Shrinkable, Stack,
},
geometry::vector::vec2f,
text_layout::ClipConfig,
ui_components::{
button::{ButtonVariant, TextAndIcon, TextAndIconAlignment},
components::{Coords, UiComponent, UiComponentStyles},
Expand Down Expand Up @@ -68,6 +69,7 @@ pub struct FilterableDropdown<A: Action + Clone> {
menu_width: Option<f32>,
vertical_margin: f32,
top_bar_height: f32,
top_bar_text_clip_config: Option<ClipConfig>,
/// See `Dropdown::use_overlay_layer`. Mirrors the same opt-out for
/// `FilterableDropdown` callers (the orchestrate environment
/// picker) that need to render in the parent's Normal layer
Expand Down Expand Up @@ -130,6 +132,7 @@ where
menu_width: None,
vertical_margin: DROPDOWN_PADDING,
top_bar_height: TOP_MENU_BAR_HEIGHT,
top_bar_text_clip_config: None,
use_overlay_layer: true,
}
}
Expand Down Expand Up @@ -321,6 +324,10 @@ where
self.top_bar_max_width = max_width;
}

pub fn set_top_bar_text_clip_config(&mut self, clip_config: ClipConfig) {
self.top_bar_text_clip_config = Some(clip_config);
}

pub fn set_menu_width(&mut self, width: f32, ctx: &mut ViewContext<Self>) {
self.menu_width = Some(width);
self.dropdown.update(ctx, |menu, ctx| {
Expand Down Expand Up @@ -424,21 +431,24 @@ where
},
};

let mut label = TextAndIcon::new(
TextAndIconAlignment::TextFirst,
selected_item_text,
icons::Icon::ChevronDown
.to_warpui_icon(appearance.theme().active_ui_text_color()),
self.main_axis_size,
MainAxisAlignment::SpaceBetween,
vec2f(15., 15.),
)
.with_inner_padding(10.);
if let Some(clip_config) = self.top_bar_text_clip_config {
label = label.with_text_clip_config(clip_config);
}

let mut top_bar = appearance
.ui_builder()
.button(self.button_variant, self.top_bar_mouse_state.clone())
.with_text_and_icon_label(
TextAndIcon::new(
TextAndIconAlignment::TextFirst,
selected_item_text,
icons::Icon::ChevronDown
.to_warpui_icon(appearance.theme().active_ui_text_color()),
self.main_axis_size,
MainAxisAlignment::SpaceBetween,
vec2f(15., 15.),
)
.with_inner_padding(10.),
)
.with_text_and_icon_label(label)
.with_style(self.style_override.unwrap_or(UiComponentStyles {
padding: Some(Coords {
top: 5.,
Expand Down
14 changes: 13 additions & 1 deletion crates/warpui_core/src/ui_components/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::elements::{
OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Shrinkable, Stack,
};
use crate::geometry::vector::Vector2F;
use crate::text_layout::ClipConfig;

use crate::platform::Cursor;
use crate::{
Expand Down Expand Up @@ -43,6 +44,7 @@ pub struct TextAndIcon {
/// Padding between the text and the icon.
padding: f32,
icon_size: Vector2F,
text_clip_config: Option<ClipConfig>,
}

impl TextAndIcon {
Expand All @@ -62,13 +64,19 @@ impl TextAndIcon {
icon,
padding: 0.,
icon_size,
text_clip_config: None,
}
}

pub fn with_inner_padding(mut self, padding: f32) -> Self {
self.padding = padding;
self
}

pub fn with_text_clip_config(mut self, clip_config: ClipConfig) -> Self {
self.text_clip_config = Some(clip_config);
self
}
}

enum ButtonLabel {
Expand Down Expand Up @@ -307,9 +315,13 @@ impl Button {
}
}
ButtonLabel::TextAndIcon(text_and_icon) => {
let mut span = Span::new(text_and_icon.text, styles);
if let Some(clip_config) = text_and_icon.text_clip_config {
span = span.with_clip_config(clip_config);
}
let text = Shrinkable::new(
1.,
Container::new(Span::new(text_and_icon.text, styles).build().finish()).finish(),
Container::new(span.build().finish()).finish(),
)
.finish();
let icon = if let Some(color) = styles.font_color {
Expand Down
16 changes: 16 additions & 0 deletions crates/warpui_core/src/ui_components/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::elements::{Highlight, HighlightedRange, DEFAULT_UI_LINE_HEIGHT_RATIO}
use crate::{
elements::{Container, Element, Text},
fonts::Properties,
text_layout::ClipConfig,
ui_components::components::{UiComponent, UiComponentStyles},
};
use itertools::Itertools;
Expand All @@ -15,6 +16,7 @@ pub struct WrappableText {
wrap: bool,
line_height_ratio: f32,
highlights: Vec<HighlightedRange>,
clip_config: Option<ClipConfig>,
/// Whether the text is selectable when rendered as a descendant of a [`SelectableArea`].
is_selectable: bool,
}
Expand All @@ -27,6 +29,7 @@ impl WrappableText {
wrap: soft_wrap,
line_height_ratio: DEFAULT_UI_LINE_HEIGHT_RATIO,
highlights: vec![],
clip_config: None,
is_selectable: true,
}
}
Expand All @@ -51,6 +54,11 @@ impl WrappableText {
self.is_selectable = is_selectable;
self
}

pub fn with_clip_config(mut self, clip_config: ClipConfig) -> Self {
self.clip_config = Some(clip_config);
self
}
}

impl UiComponent for WrappableText {
Expand All @@ -71,6 +79,9 @@ impl UiComponent for WrappableText {
if let Some(weight) = styles.font_weight {
text = text.with_style(Properties::default().weight(weight))
}
if let Some(clip_config) = self.clip_config {
text = text.with_clip(clip_config);
}

// The text element assumes that highlights are sorted by character index.
text = text.with_highlights(
Expand Down Expand Up @@ -132,6 +143,11 @@ impl Span {
self.text.is_selectable = is_selectable;
self
}

pub fn with_clip_config(mut self, clip_config: ClipConfig) -> Self {
self.text = self.text.with_clip_config(clip_config);
self
}
}

impl UiComponent for Span {
Expand Down