mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 05:40:03 -04:00
Multiple mouse button support (#135)
Add support for primary, secondary and middle mouse buttons. Also improve ability to click things in low FPS situations.
This introduces a lot of breaking changes:
Backends/integrations now pass mouse events via the even stream.
Response has an interface of mostly methods instead of public members.
input.mouse is now input.pointer and has new interface.
* Rename 'mouse' to 'pointer' everywhere (pointer = mouse or touch)
* Make Response::clicked and Response::double_clicked into methods
* Remove Response::active and add dragged() and interact_pointer_pos()
* Support multiple mouse buttons
* Make PointerState interface all methods
* Make most members of Response private
This commit is contained in:
@@ -57,7 +57,7 @@ impl DemoWindow {
|
||||
ui.columns(self.num_columns, |cols| {
|
||||
for (i, col) in cols.iter_mut().enumerate() {
|
||||
col.label(format!("Column {} out of {}", i + 1, self.num_columns));
|
||||
if i + 1 == self.num_columns && col.button("Delete this").clicked {
|
||||
if i + 1 == self.num_columns && col.button("Delete this").clicked() {
|
||||
self.num_columns -= 1;
|
||||
}
|
||||
}
|
||||
@@ -287,15 +287,15 @@ impl LayoutDemo {
|
||||
|
||||
pub fn content_ui(&mut self, ui: &mut Ui) {
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Top-down").clicked {
|
||||
if ui.button("Top-down").clicked() {
|
||||
*self = Default::default();
|
||||
}
|
||||
if ui.button("Top-down, centered and justified").clicked {
|
||||
if ui.button("Top-down, centered and justified").clicked() {
|
||||
*self = Default::default();
|
||||
self.cross_align = Align::Center;
|
||||
self.cross_justify = true;
|
||||
}
|
||||
if ui.button("Horizontal wrapped").clicked {
|
||||
if ui.button("Horizontal wrapped").clicked() {
|
||||
*self = Default::default();
|
||||
self.main_dir = Direction::LeftToRight;
|
||||
self.cross_align = Align::Center;
|
||||
@@ -391,7 +391,7 @@ impl Tree {
|
||||
if depth > 0
|
||||
&& ui
|
||||
.add(Button::new("delete").text_color(Color32::RED))
|
||||
.clicked
|
||||
.clicked()
|
||||
{
|
||||
return Action::Delete;
|
||||
}
|
||||
@@ -409,7 +409,7 @@ impl Tree {
|
||||
})
|
||||
.collect();
|
||||
|
||||
if ui.button("+").clicked {
|
||||
if ui.button("+").clicked() {
|
||||
self.0.push(Tree::default());
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ impl Default for Demos {
|
||||
fn default() -> Self {
|
||||
let demos: Vec<Box<dyn super::Demo>> = vec![
|
||||
Box::new(super::WidgetGallery::default()),
|
||||
Box::new(super::input_test::InputTest::default()),
|
||||
Box::new(super::FontBook::default()),
|
||||
Box::new(super::Painting::default()),
|
||||
Box::new(super::DancingStrings::default()),
|
||||
@@ -86,7 +87,7 @@ impl DemoWindows {
|
||||
|
||||
ui.separator();
|
||||
|
||||
if ui.button("Organize windows").clicked {
|
||||
if ui.button("Organize windows").clicked() {
|
||||
ui.ctx().memory().reset_areas();
|
||||
}
|
||||
});
|
||||
@@ -255,13 +256,13 @@ fn show_menu_bar(ui: &mut Ui) {
|
||||
|
||||
menu::bar(ui, |ui| {
|
||||
menu::menu(ui, "File", |ui| {
|
||||
if ui.button("Organize windows").clicked {
|
||||
if ui.button("Organize windows").clicked() {
|
||||
ui.ctx().memory().reset_areas();
|
||||
}
|
||||
if ui
|
||||
.button("Clear egui memory")
|
||||
.on_hover_text("Forget scroll, collapsing headers etc")
|
||||
.clicked
|
||||
.clicked()
|
||||
{
|
||||
*ui.ctx().memory() = Default::default();
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub fn drag_source(ui: &mut Ui, id: Id, body: impl FnOnce(&mut Ui)) {
|
||||
|
||||
// Check for drags:
|
||||
let response = ui.interact(response.rect, id, Sense::drag());
|
||||
if response.hovered {
|
||||
if response.hovered() {
|
||||
ui.output().cursor_icon = CursorIcon::Grab;
|
||||
}
|
||||
} else {
|
||||
@@ -25,8 +25,8 @@ pub fn drag_source(ui: &mut Ui, id: Id, body: impl FnOnce(&mut Ui)) {
|
||||
// (anything with `Order::Tooltip` always gets an empty `Response`)
|
||||
// So this is fine!
|
||||
|
||||
if let Some(mouse_pos) = ui.input().mouse.pos {
|
||||
let delta = mouse_pos - response.rect.center();
|
||||
if let Some(pointer_pos) = ui.input().pointer.interact_pos() {
|
||||
let delta = pointer_pos - response.rect.center();
|
||||
ui.ctx().translate_layer(layer_id, delta);
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ pub fn drop_target<R>(
|
||||
let outer_rect = Rect::from_min_max(outer_rect_bounds.min, content_ui.min_rect().max + margin);
|
||||
let (rect, response) = ui.allocate_at_least(outer_rect.size(), Sense::hover());
|
||||
|
||||
let style = if is_being_dragged && can_accept_what_is_being_dragged && response.hovered {
|
||||
let style = if is_being_dragged && can_accept_what_is_being_dragged && response.hovered() {
|
||||
ui.style().visuals.widgets.active
|
||||
} else if is_being_dragged && can_accept_what_is_being_dragged {
|
||||
ui.style().visuals.widgets.inactive
|
||||
@@ -126,8 +126,7 @@ impl super::View for DragAndDropDemo {
|
||||
ui.label(item);
|
||||
});
|
||||
|
||||
let this_item_being_dragged = ui.memory().is_being_dragged(item_id);
|
||||
if this_item_being_dragged {
|
||||
if ui.memory().is_being_dragged(item_id) {
|
||||
source_col_row = Some((col_idx, row_idx));
|
||||
}
|
||||
}
|
||||
@@ -135,7 +134,7 @@ impl super::View for DragAndDropDemo {
|
||||
.1;
|
||||
|
||||
let is_being_dragged = ui.memory().is_anything_being_dragged();
|
||||
if is_being_dragged && can_accept_what_is_being_dragged && response.hovered {
|
||||
if is_being_dragged && can_accept_what_is_being_dragged && response.hovered() {
|
||||
drop_col = Some(col_idx);
|
||||
}
|
||||
}
|
||||
@@ -143,7 +142,7 @@ impl super::View for DragAndDropDemo {
|
||||
|
||||
if let Some((source_col, source_row)) = source_col_row {
|
||||
if let Some(drop_col) = drop_col {
|
||||
if ui.input().mouse.released {
|
||||
if ui.input().pointer.any_released() {
|
||||
// do the drop:
|
||||
let item = self.columns[source_col].remove(source_row);
|
||||
self.columns[drop_col].push(item);
|
||||
|
||||
@@ -31,7 +31,7 @@ impl FontBook {
|
||||
ui.label(format!("{}\nU+{:X}\n\nClick to copy", name, chr as u32));
|
||||
};
|
||||
|
||||
if ui.add(button).on_hover_ui(tooltip_ui).clicked {
|
||||
if ui.add(button).on_hover_ui(tooltip_ui).clicked() {
|
||||
ui.output().copied_text = chr.to_string();
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ impl super::View for FontBook {
|
||||
ui.label("Filter:");
|
||||
ui.text_edit_singleline(&mut self.filter);
|
||||
self.filter = self.filter.to_lowercase();
|
||||
if ui.button("x").clicked {
|
||||
if ui.button("x").clicked() {
|
||||
self.filter.clear();
|
||||
}
|
||||
});
|
||||
|
||||
52
egui_demo_lib/src/apps/demo/input_test.rs
Normal file
52
egui_demo_lib/src/apps/demo/input_test.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Default)]
|
||||
pub struct InputTest {
|
||||
info: String,
|
||||
}
|
||||
|
||||
impl super::Demo for InputTest {
|
||||
fn name(&self) -> &str {
|
||||
"🖱 Input Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
use super::View;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for InputTest {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let response = ui.add(
|
||||
egui::Button::new("Click, double-click or drag me with any mouse button")
|
||||
.sense(egui::Sense::click_and_drag()),
|
||||
);
|
||||
|
||||
let mut new_info = String::new();
|
||||
for &button in &[
|
||||
egui::PointerButton::Primary,
|
||||
egui::PointerButton::Secondary,
|
||||
egui::PointerButton::Middle,
|
||||
] {
|
||||
if response.clicked_by(button) {
|
||||
new_info += &format!("Clicked by {:?}\n", button);
|
||||
}
|
||||
if response.double_clicked_by(button) {
|
||||
new_info += &format!("Double-clicked by {:?}\n", button);
|
||||
}
|
||||
if response.dragged() && ui.input().pointer.button_down(button) {
|
||||
new_info += &format!("Dragged by {:?}\n", button);
|
||||
}
|
||||
}
|
||||
if !new_info.is_empty() {
|
||||
self.info = new_info;
|
||||
}
|
||||
|
||||
ui.label(&self.info);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ mod drag_and_drop;
|
||||
mod font_book;
|
||||
pub mod font_contents_emoji;
|
||||
pub mod font_contents_ubuntu;
|
||||
pub mod input_test;
|
||||
mod painting;
|
||||
mod scrolls;
|
||||
mod sliders;
|
||||
|
||||
@@ -21,7 +21,7 @@ impl Painting {
|
||||
ui.horizontal(|ui| {
|
||||
egui::stroke_ui(ui, &mut self.stroke, "Stroke");
|
||||
ui.separator();
|
||||
if ui.button("Clear Painting").clicked {
|
||||
if ui.button("Clear Painting").clicked() {
|
||||
self.lines.clear();
|
||||
}
|
||||
});
|
||||
@@ -38,12 +38,10 @@ impl Painting {
|
||||
|
||||
let current_line = self.lines.last_mut().unwrap();
|
||||
|
||||
if response.active {
|
||||
if let Some(mouse_pos) = ui.input().mouse.pos {
|
||||
let canvas_pos = mouse_pos - rect.min;
|
||||
if current_line.last() != Some(&canvas_pos) {
|
||||
current_line.push(canvas_pos);
|
||||
}
|
||||
if let Some(pointer_pos) = response.interact_pointer_pos() {
|
||||
let canvas_pos = pointer_pos - rect.min;
|
||||
if current_line.last() != Some(&canvas_pos) {
|
||||
current_line.push(canvas_pos);
|
||||
}
|
||||
} else if !current_line.is_empty() {
|
||||
self.lines.push(vec![]);
|
||||
|
||||
@@ -33,13 +33,13 @@ impl Scrolls {
|
||||
ui.add(Slider::usize(&mut self.track_item, 1..=50).text("Track Item"));
|
||||
});
|
||||
let (scroll_offset, _) = ui.horizontal(|ui| {
|
||||
let scroll_offset = ui.small_button("Scroll Offset").clicked;
|
||||
let scroll_offset = ui.small_button("Scroll Offset").clicked();
|
||||
ui.add(DragValue::f32(&mut self.offset).speed(1.0).suffix("px"));
|
||||
scroll_offset
|
||||
});
|
||||
|
||||
let scroll_top = ui.button("Scroll to top").clicked;
|
||||
let scroll_bottom = ui.button("Scroll to bottom").clicked;
|
||||
let scroll_top = ui.button("Scroll to top").clicked();
|
||||
let scroll_bottom = ui.button("Scroll to bottom").clicked();
|
||||
if scroll_bottom || scroll_top {
|
||||
self.tracking = false;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ impl Sliders {
|
||||
You can always see the full precision value by hovering the value.",
|
||||
);
|
||||
|
||||
if ui.button("Assign PI").clicked {
|
||||
if ui.button("Assign PI").clicked() {
|
||||
self.value = std::f64::consts::PI;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
||||
let (rect, response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
|
||||
|
||||
// 3. Interact: Time to check for clicks!.
|
||||
if response.clicked {
|
||||
if response.clicked() {
|
||||
*on = !*on;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ pub fn toggle(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
||||
fn toggle_compact(ui: &mut egui::Ui, on: &mut bool) -> egui::Response {
|
||||
let desired_size = ui.style().spacing.interact_size;
|
||||
let (rect, response) = ui.allocate_exact_size(desired_size, egui::Sense::click());
|
||||
*on ^= response.clicked; // toggle if clicked
|
||||
*on ^= response.clicked(); // toggle if clicked
|
||||
|
||||
let how_on = ui.ctx().animate_bool(response.id, *on);
|
||||
let visuals = ui.style().interact(&response);
|
||||
|
||||
@@ -115,7 +115,7 @@ impl super::View for WidgetGallery {
|
||||
ui.end_row();
|
||||
|
||||
ui.label("Button:");
|
||||
if ui.button("Toggle boolean").clicked {
|
||||
if ui.button("Toggle boolean").clicked() {
|
||||
*boolean = !*boolean;
|
||||
}
|
||||
ui.end_row();
|
||||
@@ -123,7 +123,7 @@ impl super::View for WidgetGallery {
|
||||
ui.label("ImageButton:");
|
||||
if ui
|
||||
.add(egui::ImageButton::new(egui::TextureId::Egui, [24.0, 16.0]))
|
||||
.clicked
|
||||
.clicked()
|
||||
{
|
||||
*boolean = !*boolean;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ impl Widgets {
|
||||
if ui
|
||||
.add(Button::new("Click me").enabled(self.button_enabled))
|
||||
.on_hover_text("This will just increase a counter.")
|
||||
.clicked
|
||||
.clicked()
|
||||
{
|
||||
self.count += 1;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ impl Widgets {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Single line text input:");
|
||||
let response = ui.text_edit_singleline(&mut self.single_line_text_input);
|
||||
if response.lost_kb_focus {
|
||||
if response.lost_kb_focus() {
|
||||
// The user pressed enter.
|
||||
}
|
||||
});
|
||||
|
||||
@@ -113,8 +113,8 @@ fn ui_url(ui: &mut egui::Ui, frame: &mut epi::Frame<'_>, url: &mut String) -> Op
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("URL:");
|
||||
trigger_fetch |= ui.text_edit_singleline(url).lost_kb_focus;
|
||||
trigger_fetch |= ui.button("GET").clicked;
|
||||
trigger_fetch |= ui.text_edit_singleline(url).lost_kb_focus();
|
||||
trigger_fetch |= ui.button("GET").clicked();
|
||||
});
|
||||
|
||||
if frame.is_web() {
|
||||
@@ -122,14 +122,14 @@ fn ui_url(ui: &mut egui::Ui, frame: &mut epi::Frame<'_>, url: &mut String) -> Op
|
||||
}
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("Source code for this example").clicked {
|
||||
if ui.button("Source code for this example").clicked() {
|
||||
*url = format!(
|
||||
"https://raw.githubusercontent.com/emilk/egui/master/{}",
|
||||
file!()
|
||||
);
|
||||
trigger_fetch = true;
|
||||
}
|
||||
if ui.button("Random image").clicked {
|
||||
if ui.button("Random image").clicked() {
|
||||
let seed = ui.input().time;
|
||||
let width = 640;
|
||||
let height = 480;
|
||||
@@ -170,7 +170,7 @@ fn ui_resouce(
|
||||
|
||||
if let Some(text) = &response.text {
|
||||
let tooltip = "Click to copy the response body";
|
||||
if ui.button("📋").on_hover_text(tooltip).clicked {
|
||||
if ui.button("📋").on_hover_text(tooltip).clicked() {
|
||||
ui.output().copied_text = text.clone();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user