[refactor] unify InteractInfo and GuiResponse to Response

This commit is contained in:
Emil Ernerfeldt 2020-08-30 08:52:42 +02:00
parent 843074eb7d
commit c23dfd155c
15 changed files with 212 additions and 294 deletions

View file

@ -140,7 +140,7 @@ impl Area {
}
}
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) -> InteractInfo {
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) -> Response {
let prepared = self.begin(ctx);
let mut content_ui = prepared.content_ui(ctx);
add_contents(&mut content_ui);
@ -166,7 +166,7 @@ impl Prepared {
)
}
pub(crate) fn end(self, ctx: &Arc<Context>, content_ui: Ui) -> InteractInfo {
pub(crate) fn end(self, ctx: &Arc<Context>, content_ui: Ui) -> Response {
let Prepared {
layer,
mut state,
@ -183,11 +183,11 @@ impl Prepared {
} else {
None
};
let move_interact =
let move_response =
ctx.interact(layer, clip_rect, rect, interact_id, Sense::click_and_drag());
let input = ctx.input();
if move_interact.active {
if move_response.active {
state.pos += input.mouse.delta;
state.vel = input.mouse.velocity;
} else {
@ -219,7 +219,7 @@ impl Prepared {
// &format!("Area size: {:?}", state.size),
// );
if move_interact.active
if move_response.active
|| mouse_pressed_on_area(ctx, layer)
|| !ctx.memory().areas.visible_last_frame(&layer)
{
@ -228,7 +228,7 @@ impl Prepared {
}
ctx.memory().areas.set_state(layer, state);
move_interact
move_response
}
}

View file

@ -69,11 +69,11 @@ impl State {
}
/// Paint the arrow icon that indicated if the region is open or not
pub fn paint_icon(&self, ui: &mut Ui, interact: &InteractInfo) {
let stroke_color = ui.style().interact(interact).stroke_color;
let stroke_width = ui.style().interact(interact).stroke_width;
pub fn paint_icon(&self, ui: &mut Ui, response: &Response) {
let stroke_color = ui.style().interact(response).stroke_color;
let stroke_width = ui.style().interact(response).stroke_width;
let rect = interact.rect;
let rect = response.rect;
let openness = self.openness(ui);
@ -135,10 +135,10 @@ impl State {
r
}))
} else if self.open {
let r_interact = ui.add_custom(add_contents);
let full_size = r_interact.1.size();
let ret_rect = ui.add_custom(add_contents);
let full_size = ret_rect.1.size();
self.open_height = Some(full_size.y);
Some(r_interact)
Some(ret_rect)
} else {
None
}
@ -211,27 +211,27 @@ impl CollapsingHeader {
);
let rect = ui.allocate_space(size);
let interact = ui.interact(rect, id, Sense::click());
let text_pos = pos2(text_pos.x, interact.rect.center().y - galley.size.y / 2.0);
let response = ui.interact(rect, id, Sense::click());
let text_pos = pos2(text_pos.x, response.rect.center().y - galley.size.y / 2.0);
let mut state = State::from_memory_with_default_open(ui.ctx(), id, default_open);
if interact.clicked {
if response.clicked {
state.toggle(ui);
}
let bg_index = ui.painter().add(PaintCmd::Noop);
{
let (mut icon_rect, _) = ui.style().icon_rectangles(interact.rect);
let (mut icon_rect, _) = ui.style().icon_rectangles(response.rect);
icon_rect.set_center(pos2(
interact.rect.left() + ui.style().indent / 2.0,
interact.rect.center().y,
response.rect.left() + ui.style().indent / 2.0,
response.rect.center().y,
));
let icon_interact = InteractInfo {
let icon_response = Response {
rect: icon_rect,
..interact
..response.clone()
};
state.paint_icon(ui, &icon_interact);
state.paint_icon(ui, &icon_response);
}
let painter = ui.painter();
@ -239,16 +239,16 @@ impl CollapsingHeader {
text_pos,
galley,
label.text_style,
ui.style().interact(&interact).stroke_color,
ui.style().interact(&response).stroke_color,
);
painter.set(
bg_index,
PaintCmd::Rect {
corner_radius: ui.style().interact(&interact).corner_radius,
fill: ui.style().interact(&interact).bg_fill,
corner_radius: ui.style().interact(&response).corner_radius,
fill: ui.style().interact(&response).bg_fill,
outline: None,
rect: interact.rect,
rect: response.rect,
},
);
@ -257,8 +257,8 @@ impl CollapsingHeader {
pub fn show<R>(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> Option<R> {
let Prepared { id, mut state } = self.begin(ui);
let r_interact = state.add_contents(ui, |ui| ui.indent(id, add_contents).0);
let ret = r_interact.map(|ri| ri.0);
let ret_response = state.add_contents(ui, |ui| ui.indent(id, add_contents).0);
let ret = ret_response.map(|ri| ri.0);
ui.memory().collapsing_headers.insert(id, state);
ret
}

View file

@ -18,7 +18,7 @@ pub fn show_popup(
id: Id,
window_pos: Pos2,
add_contents: impl FnOnce(&mut Ui),
) -> InteractInfo {
) -> Response {
use containers::*;
Area::new(id)
.order(Order::Foreground)

View file

@ -119,7 +119,7 @@ impl Resize {
struct Prepared {
id: Id,
state: State,
corner_interact: Option<InteractInfo>,
corner_response: Option<Response>,
content_ui: Ui,
}
@ -143,19 +143,19 @@ impl Resize {
let position = ui.available().min;
let corner_interact = if self.resizable {
let corner_response = if self.resizable {
// Resize-corner:
let corner_size = Vec2::splat(ui.style().resize_corner_size);
let corner_rect =
Rect::from_min_size(position + state.desired_size - corner_size, corner_size);
let corner_interact = ui.interact(corner_rect, id.with("corner"), Sense::drag());
let corner_response = ui.interact(corner_rect, id.with("corner"), Sense::drag());
if corner_interact.active {
if corner_response.active {
if let Some(mouse_pos) = ui.input().mouse.pos {
state.desired_size = mouse_pos - position + 0.5 * corner_interact.rect.size();
state.desired_size = mouse_pos - position + 0.5 * corner_response.rect.size();
}
}
Some(corner_interact)
Some(corner_response)
} else {
None
};
@ -188,7 +188,7 @@ impl Resize {
Prepared {
id,
state,
corner_interact,
corner_response,
content_ui,
}
}
@ -204,7 +204,7 @@ impl Resize {
let Prepared {
id,
mut state,
corner_interact,
corner_response,
content_ui,
} = prepared;
@ -229,7 +229,7 @@ impl Resize {
// ------------------------------
if self.outline && corner_interact.is_some() {
if self.outline && corner_response.is_some() {
let rect = Rect::from_min_size(content_ui.top_left(), state.desired_size);
let rect = rect.expand(2.0); // breathing room for content
ui.painter().add(paint::PaintCmd::Rect {
@ -240,10 +240,10 @@ impl Resize {
});
}
if let Some(corner_interact) = corner_interact {
paint_resize_corner(ui, &corner_interact);
if let Some(corner_response) = corner_response {
paint_resize_corner(ui, &corner_response);
if corner_interact.hovered || corner_interact.active {
if corner_response.hovered || corner_response.active {
ui.ctx().output().cursor_icon = CursorIcon::ResizeNwSe;
}
}
@ -267,10 +267,10 @@ impl Resize {
use crate::paint::LineStyle;
pub fn paint_resize_corner(ui: &mut Ui, interact: &InteractInfo) {
let color = ui.style().interact(interact).stroke_color;
let width = ui.style().interact(interact).stroke_width;
paint_resize_corner_with_style(ui, &interact.rect, LineStyle::new(width, color));
pub fn paint_resize_corner(ui: &mut Ui, response: &Response) {
let color = ui.style().interact(response).stroke_color;
let width = ui.style().interact(response).stroke_width;
paint_resize_corner_with_style(ui, &response.rect, LineStyle::new(width, color));
}
pub fn paint_resize_corner_with_style(ui: &mut Ui, rect: &Rect, style: LineStyle) {

View file

@ -175,8 +175,8 @@ impl Prepared {
if content_is_too_small {
// Drag contents to scroll (for touch screens mostly):
let content_interact = ui.interact(inner_rect, id.with("area"), Sense::drag());
if content_interact.active {
let content_response = ui.interact(inner_rect, id.with("area"), Sense::drag());
if content_response.active {
state.offset.y -= ui.input().mouse.delta.y;
}
}
@ -225,26 +225,26 @@ impl Prepared {
// intentionally use same id for inside and outside of handle
let interact_id = id.with("vertical");
let mut interact = ui.interact(handle_rect, interact_id, Sense::click_and_drag());
let mut response = ui.interact(handle_rect, interact_id, Sense::click_and_drag());
if let Some(mouse_pos) = ui.input().mouse.pos {
if interact.active {
if response.active {
if inner_rect.top() <= mouse_pos.y && mouse_pos.y <= inner_rect.bottom() {
state.offset.y +=
ui.input().mouse.delta.y * content_size.y / inner_rect.height();
}
} else {
// Check for mouse down outside handle:
let scroll_bg_interact =
let scroll_bg_response =
ui.interact(outer_scroll_rect, interact_id, Sense::click_and_drag());
if scroll_bg_interact.active {
if scroll_bg_response.active {
// Center scroll at mouse pos:
let mpos_top = mouse_pos.y - handle_rect.height() / 2.0;
state.offset.y = remap(mpos_top, top..=bottom, 0.0..=content_size.y);
}
interact = interact.union(scroll_bg_interact);
response = response.union(scroll_bg_response);
}
}
@ -265,8 +265,8 @@ impl Prepared {
}
let style = ui.style();
let handle_fill = style.interact(&interact).fill;
let handle_outline = style.interact(&interact).rect_outline;
let handle_fill = style.interact(&response).fill;
let handle_outline = style.interact(&response).rect_outline;
ui.painter().add(paint::PaintCmd::Rect {
rect: outer_scroll_rect,

View file

@ -156,11 +156,7 @@ impl<'open> Window<'open> {
}
impl<'open> Window<'open> {
pub fn show(
self,
ctx: &Arc<Context>,
add_contents: impl FnOnce(&mut Ui),
) -> Option<InteractInfo> {
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) -> Option<Response> {
self.show_impl(ctx, Box::new(add_contents))
}
@ -168,7 +164,7 @@ impl<'open> Window<'open> {
self,
ctx: &Arc<Context>,
add_contents: Box<dyn FnOnce(&mut Ui) + 'c>,
) -> Option<InteractInfo> {
) -> Option<Response> {
let Window {
title_label,
open,
@ -309,9 +305,9 @@ impl<'open> Window<'open> {
);
}
}
let full_interact = area.end(ctx, area_content_ui);
let full_response = area.end(ctx, area_content_ui);
Some(full_interact)
Some(full_response)
}
}
@ -603,11 +599,11 @@ fn show_title_bar(
ui.allocate_space(vec2(0.0, 0.0)); // HACK: will add left spacing
let rect = ui.allocate_space(Vec2::splat(button_size));
let collapse_button_interact = ui.interact(rect, collapsing_id, Sense::click());
if collapse_button_interact.clicked {
let collapse_button_response = ui.interact(rect, collapsing_id, Sense::click());
if collapse_button_response.clicked {
collapsing.toggle(ui);
}
collapsing.paint_icon(ui, &collapse_button_interact);
collapsing.paint_icon(ui, &collapse_button_response);
}
let title_galley = title_label.layout(ui);
@ -688,7 +684,7 @@ impl TitleBar {
}
}
fn close_button_ui(&self, ui: &mut Ui) -> InteractInfo {
fn close_button_ui(&self, ui: &mut Ui) -> Response {
let button_size = ui.style().start_icon_width;
let button_rect = Rect::from_min_size(
pos2(
@ -702,13 +698,13 @@ impl TitleBar {
}
}
fn close_button(ui: &mut Ui, rect: Rect) -> InteractInfo {
fn close_button(ui: &mut Ui, rect: Rect) -> Response {
let close_id = ui.make_child_id("window_close_button");
let interact = ui.interact(rect, close_id, Sense::click());
ui.expand_to_include_child(interact.rect);
let response = ui.interact(rect, close_id, Sense::click());
ui.expand_to_include_child(response.rect);
let stroke_color = ui.style().interact(&interact).stroke_color;
let stroke_width = ui.style().interact(&interact).stroke_width;
let stroke_color = ui.style().interact(&response).stroke_color;
let stroke_width = ui.style().interact(&response).stroke_width;
ui.painter().line_segment(
[rect.left_top(), rect.right_bottom()],
(stroke_width, stroke_color),
@ -717,5 +713,5 @@ fn close_button(ui: &mut Ui, rect: Rect) -> InteractInfo {
[rect.right_top(), rect.left_bottom()],
(stroke_width, stroke_color),
);
interact
response
}

View file

@ -347,14 +347,15 @@ impl Context {
}
}
pub fn interact(
&self,
/// Use `ui.interact` instead
pub(crate) fn interact(
self: &Arc<Self>,
layer: Layer,
clip_rect: Rect,
rect: Rect,
interaction_id: Option<Id>,
sense: Sense,
) -> InteractInfo {
) -> Response {
let interact_rect = rect.expand2(0.5 * self.style().item_spacing); // make it easier to click. TODO: nice way to do this
let hovered = self.contains_mouse(layer, clip_rect, interact_rect);
let has_kb_focus = interaction_id
@ -363,7 +364,8 @@ impl Context {
if interaction_id.is_none() || sense == Sense::nothing() {
// Not interested in input:
return InteractInfo {
return Response {
ctx: self.clone(),
sense,
rect,
hovered,
@ -385,7 +387,8 @@ impl Context {
if self.input.mouse.pressed {
if hovered {
let mut info = InteractInfo {
let mut response = Response {
ctx: self.clone(),
sense,
rect,
hovered: true,
@ -398,7 +401,7 @@ impl Context {
if sense.click && memory.interaction.click_id.is_none() {
// start of a click
memory.interaction.click_id = Some(interaction_id);
info.active = true;
response.active = true;
}
if sense.drag
@ -408,13 +411,14 @@ impl Context {
memory.interaction.drag_id = Some(interaction_id);
memory.interaction.drag_is_window = false;
memory.window_interaction = None; // HACK: stop moving windows (if any)
info.active = true;
response.active = true;
}
info
response
} else {
// miss
InteractInfo {
Response {
ctx: self.clone(),
sense,
rect,
hovered,
@ -426,7 +430,8 @@ impl Context {
}
} else if self.input.mouse.released {
let clicked = hovered && active && self.input.mouse.could_be_click;
InteractInfo {
Response {
ctx: self.clone(),
sense,
rect,
hovered,
@ -436,7 +441,8 @@ impl Context {
has_kb_focus,
}
} else if self.input.mouse.down {
InteractInfo {
Response {
ctx: self.clone(),
sense,
rect,
hovered: hovered && active,
@ -446,7 +452,8 @@ impl Context {
has_kb_focus,
}
} else {
InteractInfo {
Response {
ctx: self.clone(),
sense,
rect,
hovered,

View file

@ -625,8 +625,8 @@ impl Painting {
fn content(&mut self, ui: &mut Ui) {
let rect = ui.allocate_space(ui.available_finite().size());
let interact = ui.interact(rect, ui.id(), Sense::drag());
let rect = interact.rect;
let response = ui.interact(rect, ui.id(), Sense::drag());
let rect = response.rect;
let clip_rect = ui.clip_rect().intersect(rect); // Make sure we don't paint out of bounds
let painter = Painter::new(ui.ctx().clone(), ui.layer(), clip_rect);
@ -636,7 +636,7 @@ impl Painting {
let current_line = self.lines.last_mut().unwrap();
if interact.active {
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) {
@ -669,7 +669,7 @@ use crate::layout::*;
#[cfg_attr(feature = "serde", serde(default))]
struct LayoutDemo {
dir: Direction,
align: Option<Align>, // None == jusitifed
align: Option<Align>, // None == justified
reversed: bool,
}
@ -735,7 +735,7 @@ impl LayoutDemo {
}
if ui
.add(RadioButton::new(self.align == None, "Justified"))
.tooltip_text("Try to fill full width/heigth (e.g. buttons)")
.tooltip_text("Try to fill full width/height (e.g. buttons)")
.clicked
{
self.align = None;

View file

@ -111,19 +111,19 @@ fn menu_impl<'c>(
button = button.fill(Some(ui.style().interact.active.fill));
}
let button_interact = ui.add(button);
let button_response = ui.add(button);
interact_with_menu_button(&mut bar_state, ui.input(), menu_id, &button_interact);
interact_with_menu_button(&mut bar_state, ui.input(), menu_id, &button_response);
if bar_state.open_menu == Some(menu_id) {
let area = Area::new(menu_id)
.order(Order::Foreground)
.fixed_pos(button_interact.rect.left_bottom());
.fixed_pos(button_response.rect.left_bottom());
let frame = Frame::menu(ui.style());
let resize = Resize::default().auto_sized().outline(false);
let menu_interact = area.show(ui.ctx(), |ui| {
let menu_response = area.show(ui.ctx(), |ui| {
frame.show(ui, |ui| {
resize.show(ui, |ui| {
let mut style = ui.style().clone();
@ -141,7 +141,7 @@ fn menu_impl<'c>(
})
});
if menu_interact.hovered && ui.input().mouse.released {
if menu_response.hovered && ui.input().mouse.released {
bar_state.open_menu = None;
}
}
@ -153,9 +153,9 @@ fn interact_with_menu_button(
bar_state: &mut BarState,
input: &InputState,
menu_id: Id,
button_interact: &GuiResponse,
button_response: &Response,
) {
if button_interact.hovered && input.mouse.pressed {
if button_response.hovered && input.mouse.pressed {
if bar_state.open_menu.is_some() {
bar_state.open_menu = None;
} else {
@ -164,7 +164,7 @@ fn interact_with_menu_button(
}
}
if button_interact.hovered && input.mouse.released && bar_state.open_menu.is_some() {
if button_response.hovered && input.mouse.released && bar_state.open_menu.is_some() {
let time_since_open = input.time - bar_state.open_time;
if time_since_open < 0.4 {
// A quick click
@ -176,7 +176,7 @@ fn interact_with_menu_button(
}
}
if button_interact.hovered && bar_state.open_menu.is_some() {
if button_response.hovered && bar_state.open_menu.is_some() {
bar_state.open_menu = Some(menu_id);
}
}

View file

@ -152,12 +152,12 @@ impl Default for Interact {
}
impl Interact {
pub fn style(&self, interact: &InteractInfo) -> &WidgetStyle {
if interact.active || interact.has_kb_focus {
pub fn style(&self, response: &Response) -> &WidgetStyle {
if response.active || response.has_kb_focus {
&self.active
} else if interact.sense == Sense::nothing() {
} else if response.sense == Sense::nothing() {
&self.disabled
} else if interact.hovered {
} else if response.hovered {
&self.hovered
} else {
&self.inactive
@ -216,9 +216,10 @@ impl Default for MenuBar {
}
impl Style {
// TODO: rename style.interact() to something better
/// Use this style for interactive things
pub fn interact(&self, interact: &InteractInfo) -> &WidgetStyle {
self.interact.style(interact)
pub fn interact(&self, response: &Response) -> &WidgetStyle {
self.interact.style(response)
}
/// Returns small icon rectangle and big icon rectangle

View file

@ -46,75 +46,16 @@ impl Default for CursorIcon {
// ----------------------------------------------------------------------------
/// The result of an interaction.
///
/// For instance, this lets you know whether or not a widget has been clicked this frame.
#[derive(Clone, Copy, Debug)]
// #[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct InteractInfo {
// IN:
/// The region of the screen we are talking about
pub rect: Rect,
/// The senses (click or drag) that the widget is interested in (if any).
pub sense: Sense,
// OUT:
/// The mouse is hovering above this thing
pub hovered: bool,
/// The mouse pressed this thing earlier, and now released on this thing too.
pub clicked: bool,
pub double_clicked: bool,
/// The mouse is interacting with this thing (e.g. dragging it or holding it)
pub active: bool,
/// This widget has the keyboard focus (i.e. is receiving key pressed)
pub has_kb_focus: bool,
}
impl InteractInfo {
pub fn nothing() -> Self {
Self {
rect: Rect::nothing(),
sense: Sense::nothing(),
hovered: false,
clicked: false,
double_clicked: false,
active: false,
has_kb_focus: false,
}
}
pub fn from_rect(rect: Rect) -> Self {
Self {
rect,
..Self::nothing()
}
}
pub fn union(self, other: Self) -> Self {
Self {
rect: self.rect.union(other.rect),
sense: self.sense.union(other.sense),
hovered: self.hovered || other.hovered,
clicked: self.clicked || other.clicked,
double_clicked: self.double_clicked || other.double_clicked,
active: self.active || other.active,
has_kb_focus: self.has_kb_focus || other.has_kb_focus,
}
}
}
// ----------------------------------------------------------------------------
/// The result of adding a widget to an `Ui`.
///
/// This lets you know whether or not a widget has been clicked this frame.
/// It also lets you easily show a tooltip on hover.
pub struct GuiResponse {
#[derive(Clone)]
pub struct Response {
// CONTEXT:
/// Used for optionally showing a tooltip
pub ctx: Arc<Context>,
// IN:
/// The area of the screen we are talking about
pub rect: Rect,
@ -129,6 +70,7 @@ pub struct GuiResponse {
/// The mouse clicked this thing this frame
pub clicked: bool,
/// The thing was double-clicked
pub double_clicked: bool,
/// The mouse is interacting with this thing (e.g. dragging it)
@ -136,13 +78,9 @@ pub struct GuiResponse {
/// This widget has the keyboard focus (i.e. is receiving key pressed)
pub has_kb_focus: bool,
// CONTEXT:
/// Used for optionally showing a tooltip
pub ctx: Arc<Context>,
}
impl GuiResponse {
impl Response {
/// Show some stuff if the item was hovered
pub fn tooltip(&mut self, add_contents: impl FnOnce(&mut Ui)) -> &mut Self {
if self.hovered {
@ -159,16 +97,18 @@ impl GuiResponse {
}
}
impl Into<InteractInfo> for GuiResponse {
fn into(self) -> InteractInfo {
InteractInfo {
rect: self.rect,
sense: self.sense,
hovered: self.hovered,
clicked: self.clicked,
double_clicked: self.double_clicked,
active: self.active,
has_kb_focus: self.has_kb_focus,
impl Response {
pub fn union(self, other: Self) -> Self {
assert!(Arc::ptr_eq(&self.ctx, &other.ctx));
Self {
ctx: self.ctx,
rect: self.rect.union(other.rect),
sense: self.sense.union(other.sense),
hovered: self.hovered || other.hovered,
clicked: self.clicked || other.clicked,
double_clicked: self.double_clicked || other.double_clicked,
active: self.active || other.active,
has_kb_focus: self.has_kb_focus || other.has_kb_focus,
}
}
}

View file

@ -327,12 +327,12 @@ impl Ui {
/// # Interaction
impl Ui {
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> InteractInfo {
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
self.ctx()
.interact(self.layer(), self.clip_rect(), rect, Some(id), sense)
}
pub fn interact_hover(&self, rect: Rect) -> InteractInfo {
pub fn interact_hover(&self, rect: Rect) -> Response {
self.ctx()
.interact(self.layer(), self.clip_rect(), rect, None, Sense::nothing())
}
@ -341,30 +341,6 @@ impl Ui {
self.interact_hover(rect).hovered
}
#[must_use]
pub fn response(&mut self, interact: InteractInfo) -> GuiResponse {
// TODO: unify GuiResponse and InteractInfo. They are the same thing!
let InteractInfo {
sense,
hovered,
clicked,
double_clicked,
active,
has_kb_focus,
rect,
} = interact;
GuiResponse {
sense,
hovered,
clicked,
double_clicked,
active,
has_kb_focus,
rect,
ctx: self.ctx().clone(),
}
}
// ------------------------------------------------------------------------
// Stuff that moves the cursor, i.e. allocates space in this ui!
@ -429,44 +405,43 @@ impl Ui {
/// # Adding widgets
impl Ui {
pub fn add(&mut self, widget: impl Widget) -> GuiResponse {
let interact = widget.ui(self);
self.response(interact)
pub fn add(&mut self, widget: impl Widget) -> Response {
widget.ui(self)
}
/// Shortcut for `add(Label::new(text))`
pub fn label(&mut self, label: impl Into<Label>) -> GuiResponse {
pub fn label(&mut self, label: impl Into<Label>) -> Response {
self.add(label.into())
}
/// Shortcut for `add(Label::new(text).heading())`
pub fn heading(&mut self, label: impl Into<Label>) -> GuiResponse {
pub fn heading(&mut self, label: impl Into<Label>) -> Response {
self.add(label.into().heading())
}
/// Shortcut for `add(Hyperlink::new(url))`
pub fn hyperlink(&mut self, url: impl Into<String>) -> GuiResponse {
pub fn hyperlink(&mut self, url: impl Into<String>) -> Response {
self.add(Hyperlink::new(url))
}
/// Shortcut for `add(Button::new(text))`
pub fn button(&mut self, text: impl Into<String>) -> GuiResponse {
pub fn button(&mut self, text: impl Into<String>) -> Response {
self.add(Button::new(text))
}
// Argument order matching that of Dear ImGui
/// Show a checkbox.
pub fn checkbox(&mut self, text: impl Into<String>, checked: &mut bool) -> GuiResponse {
pub fn checkbox(&mut self, text: impl Into<String>, checked: &mut bool) -> Response {
self.add(Checkbox::new(checked, text))
}
// Argument order matching that of Dear ImGui
/// Show a radio button.
pub fn radio(&mut self, text: impl Into<String>, checked: bool) -> GuiResponse {
pub fn radio(&mut self, text: impl Into<String>, checked: bool) -> Response {
self.add(RadioButton::new(checked, text))
}
pub fn text_edit(&mut self, text: &mut String) -> GuiResponse {
pub fn text_edit(&mut self, text: &mut String) -> Response {
self.add(TextEdit::new(text))
}
@ -477,7 +452,7 @@ impl Ui {
text: impl Into<String>,
current_value: &mut Value,
radio_value: Value,
) -> GuiResponse {
) -> Response {
let response = self.radio(text, *current_value == radio_value);
if response.clicked {
*current_value = radio_value;
@ -486,13 +461,13 @@ impl Ui {
}
/// Shortcut for `add(Separator::new())`
pub fn separator(&mut self) -> GuiResponse {
pub fn separator(&mut self) -> Response {
self.add(Separator::new())
}
/// Modify an angle. The given angle should be in radians, but is shown to the user in degrees.
/// The angle is NOT wrapped, so the user may select, for instance 720° = 2𝞃 = 4π
pub fn drag_angle(&mut self, radians: &mut f32) -> GuiResponse {
pub fn drag_angle(&mut self, radians: &mut f32) -> Response {
#![allow(clippy::float_cmp)]
let mut degrees = radians.to_degrees();

View file

@ -19,7 +19,7 @@ use paint::*;
/// Anything implementing Widget can be added to a Ui with `Ui::add`
pub trait Widget {
fn ui(self, ui: &mut Ui) -> InteractInfo;
fn ui(self, ui: &mut Ui) -> Response;
}
// ----------------------------------------------------------------------------
@ -123,7 +123,7 @@ macro_rules! label {
}
impl Widget for Label {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let galley = self.layout(ui);
let rect = ui.allocate_space(galley.size);
self.paint_galley(ui, rect.min, galley);
@ -174,7 +174,7 @@ impl Hyperlink {
}
impl Widget for Hyperlink {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let Hyperlink { url, text } = self;
let color = color::LIGHT_BLUE;
@ -183,18 +183,18 @@ impl Widget for Hyperlink {
let font = &ui.fonts()[text_style];
let galley = font.layout_multiline(text, ui.available().width());
let rect = ui.allocate_space(galley.size);
let interact = ui.interact(rect, id, Sense::click());
if interact.hovered {
let response = ui.interact(rect, id, Sense::click());
if response.hovered {
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
}
if interact.clicked {
if response.clicked {
ui.ctx().output().open_url = Some(url);
}
if interact.hovered {
if response.hovered {
// Underline:
for line in &galley.lines {
let pos = interact.rect.min;
let pos = response.rect.min;
let y = pos.y + line.y_max;
let y = ui.painter().round_to_pixel(y);
let min_x = pos.x + line.min_x();
@ -207,9 +207,9 @@ impl Widget for Hyperlink {
}
ui.painter()
.galley(interact.rect.min, galley, text_style, color);
.galley(response.rect.min, galley, text_style, color);
interact
response
}
}
@ -269,7 +269,7 @@ impl Button {
}
impl Widget for Button {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let Button {
text,
text_color,
@ -285,20 +285,20 @@ impl Widget for Button {
let mut size = galley.size + 2.0 * padding;
size.y = size.y.max(ui.style().clickable_diameter);
let rect = ui.allocate_space(size);
let interact = ui.interact(rect, id, sense);
let text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
let bg_fill = fill.or(ui.style().interact(&interact).bg_fill);
let response = ui.interact(rect, id, sense);
let text_cursor = response.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
let bg_fill = fill.or(ui.style().interact(&response).bg_fill);
ui.painter().add(PaintCmd::Rect {
rect: interact.rect,
corner_radius: ui.style().interact(&interact).corner_radius,
rect: response.rect,
corner_radius: ui.style().interact(&response).corner_radius,
fill: bg_fill,
outline: ui.style().interact(&interact).rect_outline,
outline: ui.style().interact(&response).rect_outline,
});
let stroke_color = ui.style().interact(&interact).stroke_color;
let stroke_color = ui.style().interact(&response).stroke_color;
let text_color = text_color.unwrap_or(stroke_color);
ui.painter()
.galley(text_cursor, galley, text_style, text_color);
interact
response
}
}
@ -329,7 +329,7 @@ impl<'a> Checkbox<'a> {
}
impl<'a> Widget for Checkbox<'a> {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let Checkbox {
checked,
text,
@ -345,21 +345,21 @@ impl<'a> Widget for Checkbox<'a> {
+ galley.size
+ ui.style().button_padding;
let rect = ui.allocate_space(size);
let interact = ui.interact(rect, id, Sense::click());
let response = ui.interact(rect, id, Sense::click());
let text_cursor =
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
if interact.clicked {
response.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
if response.clicked {
*checked = !*checked;
}
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(response.rect);
ui.painter().add(PaintCmd::Rect {
rect: big_icon_rect,
corner_radius: ui.style().interact(&interact).corner_radius,
fill: ui.style().interact(&interact).bg_fill,
outline: ui.style().interact(&interact).rect_outline,
corner_radius: ui.style().interact(&response).corner_radius,
fill: ui.style().interact(&response).bg_fill,
outline: ui.style().interact(&response).rect_outline,
});
let stroke_color = ui.style().interact(&interact).stroke_color;
let stroke_color = ui.style().interact(&response).stroke_color;
if *checked {
ui.painter().add(PaintCmd::Path {
@ -377,7 +377,7 @@ impl<'a> Widget for Checkbox<'a> {
let text_color = text_color.unwrap_or(stroke_color);
ui.painter()
.galley(text_cursor, galley, text_style, text_color);
interact
response
}
}
@ -407,7 +407,7 @@ impl RadioButton {
}
impl Widget for RadioButton {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let RadioButton {
checked,
text,
@ -422,14 +422,14 @@ impl Widget for RadioButton {
+ galley.size
+ ui.style().button_padding;
let rect = ui.allocate_space(size);
let interact = ui.interact(rect, id, Sense::click());
let response = ui.interact(rect, id, Sense::click());
let text_cursor =
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
response.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
let bg_fill = ui.style().interact(&interact).bg_fill;
let stroke_color = ui.style().interact(&interact).stroke_color;
let bg_fill = ui.style().interact(&response).bg_fill;
let stroke_color = ui.style().interact(&response).stroke_color;
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(response.rect);
let painter = ui.painter();
@ -437,7 +437,7 @@ impl Widget for RadioButton {
center: big_icon_rect.center(),
radius: big_icon_rect.width() / 2.0,
fill: bg_fill,
outline: ui.style().interact(&interact).rect_outline, // TODO
outline: ui.style().interact(&response).rect_outline, // TODO
});
if checked {
@ -451,7 +451,7 @@ impl Widget for RadioButton {
let text_color = text_color.unwrap_or(stroke_color);
painter.galley(text_cursor, galley, text_style, text_color);
interact
response
}
}
@ -499,7 +499,7 @@ impl Separator {
}
impl Widget for Separator {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let Separator {
line_width,
spacing,
@ -582,7 +582,7 @@ impl<'a> DragValue<'a> {
}
impl<'a> Widget for DragValue<'a> {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let Self {
value,
speed,
@ -617,7 +617,7 @@ impl<'a> Widget for DragValue<'a> {
} else {
ui.memory().temp_edit_string = Some(value_text);
}
response.into()
response
} else {
let button = Button::new(format!("{}{}{}", prefix, value_text, suffix))
.sense(Sense::click_and_drag())
@ -638,7 +638,7 @@ impl<'a> Widget for DragValue<'a> {
// otherwise we will just keep rounding to the same value while moving the mouse.
}
}
response.into()
response
}
}
}

View file

@ -124,7 +124,7 @@ fn x_range(rect: &Rect) -> RangeInclusive<f32> {
impl<'a> Slider<'a> {
/// Just the slider, no text
fn allocate_slide_space(&self, ui: &mut Ui, height: f32) -> InteractInfo {
fn allocate_slide_space(&self, ui: &mut Ui, height: f32) -> Response {
let id = self.id.unwrap_or_else(|| ui.make_position_id());
let desired_size = vec2(ui.available().width(), height);
let rect = ui.allocate_space(desired_size);
@ -132,15 +132,15 @@ impl<'a> Slider<'a> {
}
/// Just the slider, no text
fn slider_ui(&mut self, ui: &mut Ui, interact: InteractInfo) {
let rect = &interact.rect;
fn slider_ui(&mut self, ui: &mut Ui, response: &Response) {
let rect = &response.rect;
let x_range = x_range(rect);
let range = self.range.clone();
debug_assert!(range.start() <= range.end());
if let Some(mouse_pos) = ui.input().mouse.pos {
if interact.active {
if response.active {
let aim_radius = ui.input().aim_radius();
let new_value = crate::math::smart_aim::best_in_range_f32(
self.value_from_x_clamped(mouse_pos.x - aim_radius, x_range.clone()),
@ -171,10 +171,10 @@ impl<'a> Slider<'a> {
ui.painter().add(PaintCmd::Circle {
center: pos2(marker_center_x, rail_rect.center().y),
radius: handle_radius(rect),
fill: Some(ui.style().interact(&interact).fill),
fill: Some(ui.style().interact(response).fill),
outline: Some(LineStyle::new(
ui.style().interact(&interact).stroke_width,
ui.style().interact(&interact).stroke_color,
ui.style().interact(response).stroke_width,
ui.style().interact(response).stroke_color,
)),
});
}
@ -255,7 +255,7 @@ impl<'a> Slider<'a> {
}
impl<'a> Widget for Slider<'a> {
fn ui(mut self, ui: &mut Ui) -> InteractInfo {
fn ui(mut self, ui: &mut Ui) -> Response {
let text_style = TextStyle::Button;
let font = &ui.fonts()[text_style];
let height = font.line_spacing().max(ui.style().clickable_diameter);
@ -265,13 +265,13 @@ impl<'a> Widget for Slider<'a> {
ui.columns(2, |columns| {
let slider_ui = &mut columns[0];
let slider_interact = self.allocate_slide_space(slider_ui, height);
self.slider_ui(slider_ui, slider_interact);
let x_range = x_range(&slider_interact.rect);
let slider_response = self.allocate_slide_space(slider_ui, height);
self.slider_ui(slider_ui, &slider_response);
let x_range = x_range(&slider_response.rect);
// Place the text in line with the slider on the left:
let text_ui = &mut columns[1];
text_ui.set_desired_height(slider_interact.rect.height());
text_ui.set_desired_height(slider_response.rect.height());
text_ui.inner_layout(
Layout::horizontal(Align::Center),
text_ui.available().size(),
@ -280,12 +280,12 @@ impl<'a> Widget for Slider<'a> {
},
);
slider_interact
slider_response
})
} else {
let interact = self.allocate_slide_space(ui, height);
self.slider_ui(ui, interact);
interact
let response = self.allocate_slide_space(ui, height);
self.slider_ui(ui, &response);
response
}
}
}

View file

@ -74,7 +74,7 @@ impl<'t> TextEdit<'t> {
}
impl<'t> Widget for TextEdit<'t> {
fn ui(self, ui: &mut Ui) -> InteractInfo {
fn ui(self, ui: &mut Ui) -> Response {
let TextEdit {
text,
id,
@ -108,14 +108,14 @@ impl<'t> Widget for TextEdit<'t> {
} else {
Sense::nothing()
};
let interact = ui.interact(rect, id, sense); // TODO: implement drag-select
let response = ui.interact(rect, id, sense); // TODO: implement drag-select
if interact.clicked && enabled {
if response.clicked && enabled {
ui.memory().request_kb_focus(id);
if let Some(mouse_pos) = ui.input().mouse.pos {
state.cursor = Some(galley.char_at(mouse_pos - interact.rect.min).char_idx);
state.cursor = Some(galley.char_at(mouse_pos - response.rect.min).char_idx);
}
} else if ui.input().mouse.click || (ui.input().mouse.pressed && !interact.hovered) {
} else if ui.input().mouse.click || (ui.input().mouse.pressed && !response.hovered) {
// User clicked somewhere else
ui.memory().surrender_kb_focus(id);
}
@ -123,7 +123,7 @@ impl<'t> Widget for TextEdit<'t> {
ui.memory().surrender_kb_focus(id);
}
if interact.hovered && enabled {
if response.hovered && enabled {
ui.output().cursor_icon = CursorIcon::Text;
}
@ -179,12 +179,12 @@ impl<'t> Widget for TextEdit<'t> {
let painter = ui.painter();
{
let bg_rect = interact.rect.expand(2.0); // breathing room for content
let bg_rect = response.rect.expand(2.0); // breathing room for content
painter.add(PaintCmd::Rect {
rect: bg_rect,
corner_radius: ui.style().interact.style(&interact).corner_radius,
corner_radius: ui.style().interact(&response).corner_radius,
fill: Some(ui.style().dark_bg_color),
outline: ui.style().interact.style(&interact).rect_outline,
outline: ui.style().interact(&response).rect_outline,
});
}
@ -199,7 +199,7 @@ impl<'t> Widget for TextEdit<'t> {
if show_cursor {
if let Some(cursor) = state.cursor {
let cursor_pos = interact.rect.min + galley.char_start_pos(cursor);
let cursor_pos = response.rect.min + galley.char_start_pos(cursor);
painter.line_segment(
[cursor_pos, cursor_pos + vec2(0.0, line_spacing)],
(ui.style().text_cursor_width, color::WHITE),
@ -208,11 +208,10 @@ impl<'t> Widget for TextEdit<'t> {
}
}
let text_color =
text_color.unwrap_or_else(|| ui.style().interact.style(&interact).stroke_color);
painter.galley(interact.rect.min, galley, text_style, text_color);
let text_color = text_color.unwrap_or_else(|| ui.style().interact(&response).stroke_color);
painter.galley(response.rect.min, galley, text_style, text_color);
ui.memory().text_edit.insert(id, state);
interact
response
}
}