mirror of
https://github.com/emilk/egui.git
synced 2026-06-27 07:03:14 -04:00
Merge branch 'widget_style' of github.com:AdrienZianne/egui into widget_style
This commit is contained in:
@@ -239,7 +239,7 @@ impl ComboBox {
|
||||
let mut ir = combo_box_dyn(
|
||||
ui,
|
||||
button_id,
|
||||
selected_text,
|
||||
selected_text.clone(),
|
||||
menu_contents,
|
||||
icon,
|
||||
wrap_mode,
|
||||
@@ -247,14 +247,16 @@ impl ComboBox {
|
||||
popup_style,
|
||||
(width, height),
|
||||
);
|
||||
ir.response.widget_info(|| {
|
||||
let mut info = WidgetInfo::new(WidgetType::ComboBox);
|
||||
info.enabled = ui.is_enabled();
|
||||
info.current_text_value = Some(selected_text.text().to_owned());
|
||||
info
|
||||
});
|
||||
if let Some(label) = label {
|
||||
ir.response.widget_info(|| {
|
||||
WidgetInfo::labeled(WidgetType::ComboBox, ui.is_enabled(), label.text())
|
||||
});
|
||||
ir.response |= ui.label(label);
|
||||
} else {
|
||||
ir.response
|
||||
.widget_info(|| WidgetInfo::labeled(WidgetType::ComboBox, ui.is_enabled(), ""));
|
||||
let label_response = ui.label(label);
|
||||
ir.response = ir.response.labelled_by(label_response.id);
|
||||
ir.response |= label_response;
|
||||
}
|
||||
ir
|
||||
})
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
|
||||
|
||||
use crate::{
|
||||
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Sense, Ui, UiBuilder, UiKind,
|
||||
UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
|
||||
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
|
||||
UiKind, UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -659,6 +659,9 @@ struct Prepared {
|
||||
/// not for us to handle so we save it and restore it after this [`ScrollArea`] is done.
|
||||
saved_scroll_target: [Option<pass_state::ScrollTarget>; 2],
|
||||
|
||||
/// The response from dragging the background (if enabled)
|
||||
background_drag_response: Option<Response>,
|
||||
|
||||
animated: bool,
|
||||
}
|
||||
|
||||
@@ -772,70 +775,72 @@ impl ScrollArea {
|
||||
let viewport = Rect::from_min_size(Pos2::ZERO + state.offset, inner_size);
|
||||
let dt = ui.input(|i| i.stable_dt).at_most(0.1);
|
||||
|
||||
if scroll_source.drag
|
||||
&& ui.is_enabled()
|
||||
&& (state.content_is_too_large[0] || state.content_is_too_large[1])
|
||||
{
|
||||
// Drag contents to scroll (for touch screens mostly).
|
||||
// We must do this BEFORE adding content to the `ScrollArea`,
|
||||
// or we will steal input from the widgets we contain.
|
||||
let content_response_option = state
|
||||
.interact_rect
|
||||
.map(|rect| ui.interact(rect, id.with("area"), Sense::drag()));
|
||||
let background_drag_response =
|
||||
if scroll_source.drag && ui.is_enabled() && state.content_is_too_large.any() {
|
||||
// Drag contents to scroll (for touch screens mostly).
|
||||
// We must do this BEFORE adding content to the `ScrollArea`,
|
||||
// or we will steal input from the widgets we contain.
|
||||
let content_response_option = state
|
||||
.interact_rect
|
||||
.map(|rect| ui.interact(rect, id.with("area"), Sense::drag()));
|
||||
|
||||
if content_response_option
|
||||
.as_ref()
|
||||
.is_some_and(|response| response.dragged())
|
||||
{
|
||||
for d in 0..2 {
|
||||
if direction_enabled[d] {
|
||||
ui.input(|input| {
|
||||
state.offset[d] -= input.pointer.delta()[d];
|
||||
});
|
||||
state.scroll_stuck_to_end[d] = false;
|
||||
state.offset_target[d] = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Apply the cursor velocity to the scroll area when the user releases the drag.
|
||||
if content_response_option
|
||||
.as_ref()
|
||||
.is_some_and(|response| response.drag_stopped())
|
||||
.is_some_and(|response| response.dragged())
|
||||
{
|
||||
state.vel =
|
||||
direction_enabled.to_vec2() * ui.input(|input| input.pointer.velocity());
|
||||
}
|
||||
for d in 0..2 {
|
||||
// Kinetic scrolling
|
||||
let stop_speed = 20.0; // Pixels per second.
|
||||
let friction_coeff = 1000.0; // Pixels per second squared.
|
||||
for d in 0..2 {
|
||||
if direction_enabled[d] {
|
||||
ui.input(|input| {
|
||||
state.offset[d] -= input.pointer.delta()[d];
|
||||
});
|
||||
state.scroll_stuck_to_end[d] = false;
|
||||
state.offset_target[d] = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Apply the cursor velocity to the scroll area when the user releases the drag.
|
||||
if content_response_option
|
||||
.as_ref()
|
||||
.is_some_and(|response| response.drag_stopped())
|
||||
{
|
||||
state.vel = direction_enabled.to_vec2()
|
||||
* ui.input(|input| input.pointer.velocity());
|
||||
}
|
||||
for d in 0..2 {
|
||||
// Kinetic scrolling
|
||||
let stop_speed = 20.0; // Pixels per second.
|
||||
let friction_coeff = 1000.0; // Pixels per second squared.
|
||||
|
||||
let friction = friction_coeff * dt;
|
||||
if friction > state.vel[d].abs() || state.vel[d].abs() < stop_speed {
|
||||
state.vel[d] = 0.0;
|
||||
} else {
|
||||
state.vel[d] -= friction * state.vel[d].signum();
|
||||
// Offset has an inverted coordinate system compared to
|
||||
// the velocity, so we subtract it instead of adding it
|
||||
state.offset[d] -= state.vel[d] * dt;
|
||||
ctx.request_repaint();
|
||||
let friction = friction_coeff * dt;
|
||||
if friction > state.vel[d].abs() || state.vel[d].abs() < stop_speed {
|
||||
state.vel[d] = 0.0;
|
||||
} else {
|
||||
state.vel[d] -= friction * state.vel[d].signum();
|
||||
// Offset has an inverted coordinate system compared to
|
||||
// the velocity, so we subtract it instead of adding it
|
||||
state.offset[d] -= state.vel[d] * dt;
|
||||
ctx.request_repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the desired mouse cursors.
|
||||
if let Some(response) = content_response_option {
|
||||
if response.dragged() {
|
||||
if let Some(cursor) = on_drag_cursor {
|
||||
response.on_hover_cursor(cursor);
|
||||
// Set the desired mouse cursors.
|
||||
if let Some(response) = &content_response_option {
|
||||
if response.dragged()
|
||||
&& let Some(cursor) = on_drag_cursor
|
||||
{
|
||||
ui.ctx().set_cursor_icon(cursor);
|
||||
} else if response.hovered()
|
||||
&& let Some(cursor) = on_hover_cursor
|
||||
{
|
||||
ui.ctx().set_cursor_icon(cursor);
|
||||
}
|
||||
} else if response.hovered()
|
||||
&& let Some(cursor) = on_hover_cursor
|
||||
{
|
||||
response.on_hover_cursor(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content_response_option
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Scroll with an animation if we have a target offset (that hasn't been cleared by the code
|
||||
// above).
|
||||
@@ -888,6 +893,7 @@ impl ScrollArea {
|
||||
wheel_scroll_multiplier,
|
||||
stick_to_end,
|
||||
saved_scroll_target,
|
||||
background_drag_response,
|
||||
animated,
|
||||
}
|
||||
}
|
||||
@@ -1003,6 +1009,7 @@ impl Prepared {
|
||||
wheel_scroll_multiplier,
|
||||
stick_to_end,
|
||||
saved_scroll_target,
|
||||
background_drag_response,
|
||||
animated,
|
||||
} = self;
|
||||
|
||||
@@ -1118,7 +1125,16 @@ impl Prepared {
|
||||
);
|
||||
|
||||
let max_offset = content_size - inner_rect.size();
|
||||
let is_hovering_outer_rect = ui.rect_contains_pointer(outer_rect);
|
||||
|
||||
// Drag-to-scroll?
|
||||
let is_dragging_background = background_drag_response
|
||||
.as_ref()
|
||||
.is_some_and(|r| r.dragged());
|
||||
|
||||
let is_hovering_outer_rect = ui.rect_contains_pointer(outer_rect)
|
||||
&& ui.ctx().dragged_id().is_none()
|
||||
|| is_dragging_background;
|
||||
|
||||
if scroll_source.mouse_wheel && ui.is_enabled() && is_hovering_outer_rect {
|
||||
let always_scroll_enabled_direction = ui.style().always_scroll_the_only_direction
|
||||
&& direction_enabled[0] != direction_enabled[1];
|
||||
@@ -1204,6 +1220,7 @@ impl Prepared {
|
||||
|
||||
let is_hovering_bar_area = is_hovering_outer_rect
|
||||
&& ui.rect_contains_pointer(max_bar_rect)
|
||||
&& !is_dragging_background
|
||||
|| state.scroll_bar_interaction[d];
|
||||
|
||||
let is_hovering_bar_area_t = ui
|
||||
|
||||
@@ -1129,7 +1129,10 @@ impl Visuals {
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct Selection {
|
||||
/// Background color behind selected text and other selectable buttons.
|
||||
pub bg_fill: Color32,
|
||||
|
||||
/// Color of selected text.
|
||||
pub stroke: Stroke,
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ pub fn paint_text_selection(
|
||||
// and so we need to clone it if it is shared:
|
||||
let galley: &mut Galley = Arc::make_mut(galley);
|
||||
|
||||
let color = visuals.selection.bg_fill;
|
||||
let background_color = visuals.selection.bg_fill;
|
||||
let text_color = visuals.selection.stroke.color;
|
||||
|
||||
let [min, max] = cursor_range.sorted_cursors();
|
||||
let min = galley.layout_from_cursor(min);
|
||||
let max = galley.layout_from_cursor(max);
|
||||
@@ -53,6 +55,31 @@ pub fn paint_text_selection(
|
||||
let rect = Rect::from_min_max(pos2(left, 0.0), pos2(right, row.size.y));
|
||||
let mesh = &mut row.visuals.mesh;
|
||||
|
||||
if !row.glyphs.is_empty() {
|
||||
// Change color of the selected text:
|
||||
let first_glyph_index = if ri == min.row { min.column } else { 0 };
|
||||
let last_glyph_index = if ri == max.row {
|
||||
max.column
|
||||
} else {
|
||||
row.glyphs.len() - 1
|
||||
};
|
||||
|
||||
let first_vertex_index = row
|
||||
.glyphs
|
||||
.get(first_glyph_index)
|
||||
.map_or(row.visuals.glyph_vertex_range.start, |g| {
|
||||
g.first_vertex as _
|
||||
});
|
||||
let last_vertex_index = row
|
||||
.glyphs
|
||||
.get(last_glyph_index)
|
||||
.map_or(row.visuals.glyph_vertex_range.end, |g| g.first_vertex as _);
|
||||
|
||||
for vi in first_vertex_index..last_vertex_index {
|
||||
mesh.vertices[vi].color = text_color;
|
||||
}
|
||||
}
|
||||
|
||||
// Time to insert the selection rectangle into the row mesh.
|
||||
// It should be on top (after) of any background in the galley,
|
||||
// but behind (before) any glyphs. The row visuals has this information:
|
||||
@@ -60,7 +87,7 @@ pub fn paint_text_selection(
|
||||
|
||||
// Start by appending the selection rectangle to end of the mesh, as two triangles (= 6 indices):
|
||||
let num_indices_before = mesh.indices.len();
|
||||
mesh.add_colored_rect(rect, color);
|
||||
mesh.add_colored_rect(rect, background_color);
|
||||
assert_eq!(
|
||||
num_indices_before + 6,
|
||||
mesh.indices.len(),
|
||||
|
||||
@@ -227,22 +227,29 @@ impl<'a> Button<'a> {
|
||||
///
|
||||
/// See also [`Self::right_text`].
|
||||
#[inline]
|
||||
pub fn shortcut_text(mut self, shortcut_text: impl Into<Atom<'a>>) -> Self {
|
||||
let mut atom = shortcut_text.into();
|
||||
atom.kind = match atom.kind {
|
||||
AtomKind::Text(text) => AtomKind::Text(text.weak()),
|
||||
other => other,
|
||||
};
|
||||
pub fn shortcut_text(mut self, shortcut_text: impl IntoAtoms<'a>) -> Self {
|
||||
self.layout.push_right(Atom::grow());
|
||||
self.layout.push_right(atom);
|
||||
|
||||
for mut atom in shortcut_text.into_atoms() {
|
||||
atom.kind = match atom.kind {
|
||||
AtomKind::Text(text) => AtomKind::Text(text.weak()),
|
||||
other => other,
|
||||
};
|
||||
self.layout.push_right(atom);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Show some text on the right side of the button.
|
||||
#[inline]
|
||||
pub fn right_text(mut self, right_text: impl Into<Atom<'a>>) -> Self {
|
||||
pub fn right_text(mut self, right_text: impl IntoAtoms<'a>) -> Self {
|
||||
self.layout.push_right(Atom::grow());
|
||||
self.layout.push_right(right_text.into());
|
||||
|
||||
for atom in right_text.into_atoms() {
|
||||
self.layout.push_right(atom);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use egui_kittest::Harness;
|
||||
use egui::{Color32, accesskit::Role};
|
||||
use egui_kittest::{Harness, kittest::Queryable as _};
|
||||
|
||||
#[test]
|
||||
fn test_kerning() {
|
||||
@@ -42,7 +43,7 @@ fn test_italics() {
|
||||
harness.run();
|
||||
harness.fit_contents();
|
||||
harness.snapshot(format!(
|
||||
"image_blending/image_{theme}_x{pixels_per_point:.2}",
|
||||
"italics/image_{theme}_x{pixels_per_point:.2}",
|
||||
theme = match theme {
|
||||
egui::Theme::Dark => "dark",
|
||||
egui::Theme::Light => "light",
|
||||
@@ -51,3 +52,24 @@ fn test_italics() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text_selection() {
|
||||
let mut harness = Harness::builder().build_ui(|ui| {
|
||||
let visuals = ui.visuals_mut();
|
||||
visuals.selection.bg_fill = Color32::LIGHT_GREEN;
|
||||
visuals.selection.stroke.color = Color32::DARK_BLUE;
|
||||
|
||||
ui.label("Some varied ☺ text :)\nAnd it has a second line!");
|
||||
});
|
||||
harness.run();
|
||||
harness.fit_contents();
|
||||
|
||||
// Drag to select text:
|
||||
let label = harness.get_by_role(Role::Label);
|
||||
harness.drag_at(label.rect().lerp_inside([0.2, 0.25]));
|
||||
harness.drop_at(label.rect().lerp_inside([0.6, 0.75]));
|
||||
harness.run();
|
||||
|
||||
harness.snapshot("text_selection");
|
||||
}
|
||||
|
||||
3
crates/egui_demo_lib/tests/snapshots/text_selection.png
Normal file
3
crates/egui_demo_lib/tests/snapshots/text_selection.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:14f253fedc94985ff1431f1016d901d747e1f9948531cc6350f6615649f29056
|
||||
size 4862
|
||||
@@ -8,9 +8,7 @@ mod builder;
|
||||
mod snapshot;
|
||||
|
||||
#[cfg(feature = "snapshot")]
|
||||
pub use snapshot::*;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::time::Duration;
|
||||
pub use crate::snapshot::*;
|
||||
|
||||
mod app_kind;
|
||||
mod node;
|
||||
@@ -20,19 +18,26 @@ mod texture_to_image;
|
||||
#[cfg(feature = "wgpu")]
|
||||
pub mod wgpu;
|
||||
|
||||
pub use kittest;
|
||||
// re-exports:
|
||||
pub use {
|
||||
self::{builder::*, node::*, renderer::*},
|
||||
kittest,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fmt::{Debug, Display, Formatter},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use egui::{
|
||||
Color32, Key, Modifiers, PointerButton, Pos2, Rect, RepaintCause, Shape, Vec2, ViewportId,
|
||||
epaint::{ClippedShape, RectShape},
|
||||
style::ScrollAnimation,
|
||||
};
|
||||
use kittest::Queryable;
|
||||
|
||||
use crate::app_kind::AppKind;
|
||||
|
||||
pub use builder::*;
|
||||
pub use node::*;
|
||||
pub use renderer::*;
|
||||
|
||||
use egui::epaint::{ClippedShape, RectShape};
|
||||
use egui::style::ScrollAnimation;
|
||||
use egui::{Color32, Key, Modifiers, Pos2, Rect, RepaintCause, Shape, Vec2, ViewportId};
|
||||
use kittest::Queryable;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExceededMaxStepsError {
|
||||
pub max_steps: u64,
|
||||
@@ -598,6 +603,32 @@ impl<'a, State> Harness<'a, State> {
|
||||
self.key_combination_modifiers(modifiers, &[key]);
|
||||
}
|
||||
|
||||
/// Move mouse cursor to this position.
|
||||
pub fn hover_at(&self, pos: egui::Pos2) {
|
||||
self.event(egui::Event::PointerMoved(pos));
|
||||
}
|
||||
|
||||
/// Start dragging from a position.
|
||||
pub fn drag_at(&self, pos: egui::Pos2) {
|
||||
self.event(egui::Event::PointerButton {
|
||||
pos,
|
||||
button: PointerButton::Primary,
|
||||
pressed: true,
|
||||
modifiers: Modifiers::NONE,
|
||||
});
|
||||
}
|
||||
|
||||
/// Stop dragging and remove cursor.
|
||||
pub fn drop_at(&self, pos: egui::Pos2) {
|
||||
self.event(egui::Event::PointerButton {
|
||||
pos,
|
||||
button: PointerButton::Primary,
|
||||
pressed: false,
|
||||
modifiers: Modifiers::NONE,
|
||||
});
|
||||
self.remove_cursor();
|
||||
}
|
||||
|
||||
/// Remove the cursor from the screen.
|
||||
///
|
||||
/// Will fire a [`egui::Event::PointerGone`] event.
|
||||
|
||||
@@ -449,7 +449,8 @@ impl Rect {
|
||||
/// Linearly interpolate so that `[0, 0]` is [`Self::min`] and
|
||||
/// `[1, 1]` is [`Self::max`].
|
||||
#[inline]
|
||||
pub fn lerp_inside(&self, t: Vec2) -> Pos2 {
|
||||
pub fn lerp_inside(&self, t: impl Into<Vec2>) -> Pos2 {
|
||||
let t = t.into();
|
||||
Pos2 {
|
||||
x: lerp(self.min.x..=self.max.x, t.x),
|
||||
y: lerp(self.min.y..=self.max.y, t.y),
|
||||
|
||||
@@ -230,6 +230,7 @@ fn layout_section(
|
||||
font_ascent: font_metrics.ascent,
|
||||
uv_rect: glyph_alloc.uv_rect,
|
||||
section_index,
|
||||
first_vertex: 0, // filled in later
|
||||
});
|
||||
|
||||
paragraph.cursor_x_px += glyph_alloc.advance_width_px;
|
||||
@@ -531,6 +532,7 @@ fn replace_last_glyph_with_overflow_character(
|
||||
font_ascent: font_metrics.ascent,
|
||||
uv_rect: replacement_glyph_alloc.uv_rect,
|
||||
section_index,
|
||||
first_vertex: 0, // filled in later
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -748,7 +750,7 @@ fn tessellate_row(
|
||||
point_scale: PointScale,
|
||||
job: &LayoutJob,
|
||||
format_summary: &FormatSummary,
|
||||
row: &Row,
|
||||
row: &mut Row,
|
||||
) -> RowVisuals {
|
||||
if row.glyphs.is_empty() {
|
||||
return Default::default();
|
||||
@@ -843,8 +845,9 @@ fn add_row_backgrounds(point_scale: PointScale, job: &LayoutJob, row: &Row, mesh
|
||||
end_run(run_start.take(), last_rect.right());
|
||||
}
|
||||
|
||||
fn tessellate_glyphs(point_scale: PointScale, job: &LayoutJob, row: &Row, mesh: &mut Mesh) {
|
||||
for glyph in &row.glyphs {
|
||||
fn tessellate_glyphs(point_scale: PointScale, job: &LayoutJob, row: &mut Row, mesh: &mut Mesh) {
|
||||
for glyph in &mut row.glyphs {
|
||||
glyph.first_vertex = mesh.vertices.len() as u32;
|
||||
let uv_rect = glyph.uv_rect;
|
||||
if !uv_rect.is_nothing() {
|
||||
let mut left_top = glyph.pos + uv_rect.offset;
|
||||
|
||||
@@ -701,6 +701,9 @@ pub struct Glyph {
|
||||
/// enable the paragraph-concat optimization path without having to
|
||||
/// adjust `section_index` when concatting.
|
||||
pub(crate) section_index: u32,
|
||||
|
||||
/// Which is our first vertex in [`RowVisuals::mesh`].
|
||||
pub first_vertex: u32,
|
||||
}
|
||||
|
||||
impl Glyph {
|
||||
|
||||
@@ -61,3 +61,17 @@ fn text_edit_rtl() {
|
||||
harness.snapshot(format!("text_edit_rtl_{i}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn combobox_should_have_value() {
|
||||
let harness = Harness::new_ui(|ui| {
|
||||
egui::ComboBox::from_label("Select an option")
|
||||
.selected_text("Option 1")
|
||||
.show_ui(ui, |_ui| {});
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
harness.get_by_label("Select an option").value().as_deref(),
|
||||
Some("Option 1")
|
||||
);
|
||||
}
|
||||
|
||||
3
tests/egui_tests/tests/snapshots/button_shortcut.png
Normal file
3
tests/egui_tests/tests/snapshots/button_shortcut.png
Normal file
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5befd84158b582c79a968f36e43c7017187b364824eb4470b048d133e62f9360
|
||||
size 1600
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0f107d95fee9a5fb5fbfd2422452e1820738a84c81774587dbfa8153e91e4c73
|
||||
size 414552
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c1aebada9349f8cb4046469b0a6f9796a21f88b6724bd85cd832a40b8007409
|
||||
size 540527
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:685de2e33ff26aafa87426bcda18bb9963c2deb2a811cd0aae4450af0e245a06
|
||||
size 390735
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:cf4236b1a8f63d184cd780c334d9f996e4d47817a96a29f0d81658d2d897597f
|
||||
size 10529
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c7a63953853f526b83f80d63335b03e60258ea9a3416d19f8ed57d746b5c551d
|
||||
size 21557
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1f7d802a4de7e30f8d254cab6d9ca127866c104c1738103bc4a579917e8f42d3
|
||||
size 9850
|
||||
@@ -108,3 +108,14 @@ fn test_intrinsic_size() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_button_shortcut_text() {
|
||||
let mut harness = HarnessBuilder::default().build_ui(|ui| {
|
||||
ui.add(egui::Button::new("Click me").shortcut_text(("1", "2", "3")));
|
||||
});
|
||||
harness.run();
|
||||
harness.fit_contents();
|
||||
|
||||
harness.snapshot("button_shortcut");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use egui::accesskit::Role;
|
||||
use egui::load::SizedTexture;
|
||||
use egui::{
|
||||
Align, AtomExt as _, AtomLayout, Button, Color32, ColorImage, Direction, DragValue, Event,
|
||||
Grid, IntoAtoms as _, Layout, PointerButton, Response, Slider, Stroke, StrokeKind,
|
||||
Grid, IntoAtoms as _, Layout, PointerButton, Response, Slider, Stroke, StrokeKind, TextEdit,
|
||||
TextWrapMode, TextureHandle, TextureOptions, Ui, UiBuilder, Vec2, Widget as _, include_image,
|
||||
};
|
||||
use egui_kittest::kittest::{Queryable as _, by};
|
||||
@@ -84,6 +84,37 @@ fn widget_tests() {
|
||||
},
|
||||
&mut results,
|
||||
);
|
||||
test_widget(
|
||||
"text_edit_clip",
|
||||
|ui| {
|
||||
ui.spacing_mut().text_edit_width = 45.0;
|
||||
TextEdit::singleline(&mut "This is a very very long text".to_owned())
|
||||
.clip_text(true)
|
||||
.ui(ui)
|
||||
},
|
||||
&mut results,
|
||||
);
|
||||
test_widget(
|
||||
"text_edit_no_clip",
|
||||
|ui| {
|
||||
ui.spacing_mut().text_edit_width = 45.0;
|
||||
TextEdit::singleline(&mut "This is a very very long text".to_owned())
|
||||
.clip_text(false)
|
||||
.ui(ui)
|
||||
},
|
||||
&mut results,
|
||||
);
|
||||
test_widget(
|
||||
"text_edit_placeholder_clip",
|
||||
|ui| {
|
||||
ui.spacing_mut().text_edit_width = 45.0;
|
||||
TextEdit::singleline(&mut String::new())
|
||||
.hint_text("This is a very very long placeholder")
|
||||
.clip_text(true)
|
||||
.ui(ui)
|
||||
},
|
||||
&mut results,
|
||||
);
|
||||
|
||||
test_widget(
|
||||
"slider",
|
||||
|
||||
Reference in New Issue
Block a user