bug: Fix mouse hitboxes (#459)

Fixes the mouse hitbox checks overextending by 1. Also reverts the bandaid fix done for #458.
This commit is contained in:
Clement Tsang 2021-04-23 23:13:42 -04:00 committed by GitHub
parent fcc478a1eb
commit d4a18aea75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 211 additions and 188 deletions

View file

@ -65,8 +65,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#425](https://github.com/ClementTsang/bottom/pull/425): Fixed a bug allowing grouped mode in tree mode if already in grouped mode.
- [#458](https://github.com/ClementTsang/bottom/pull/458): Fix basic mode having really broken click bounding boxes.
## [0.5.7] - 2021-01-30
## Bug Fixes

View file

@ -2809,7 +2809,7 @@ impl App {
// Pretty dead simple - iterate through the widget map and go to the widget where the click
// is within.
// TODO: [REFACTOR] might want to refactor this, it's ugly as sin.
// TODO: [REFACTOR] might want to refactor this, it's really ugly.
// TODO: [REFACTOR] Might wanna refactor ALL state things in general, currently everything
// is grouped up as an app state. We should separate stuff like event state and gui state and etc.
@ -2826,7 +2826,8 @@ impl App {
Some((right_brc_x, right_brc_y)),
) = (bt.left_tlc, bt.left_brc, bt.right_tlc, bt.right_brc)
{
if (x >= left_tlc_x && y >= left_tlc_y) && (x <= left_brc_x && y <= left_brc_y) {
if (x >= left_tlc_x && y >= left_tlc_y) && (x < left_brc_x && y < left_brc_y) {
// Case for the left "button" in the simple arrow.
if let Some(new_widget) =
self.widget_map.get(&(bt.currently_displayed_widget_id))
{
@ -2846,8 +2847,9 @@ impl App {
return;
}
} else if (x >= right_tlc_x && y >= right_tlc_y)
&& (x <= right_brc_x && y <= right_brc_y)
&& (x < right_brc_x && y < right_brc_y)
{
// Case for the right "button" in the simple arrow.
if let Some(new_widget) =
self.widget_map.get(&(bt.currently_displayed_widget_id))
{
@ -2905,7 +2907,7 @@ impl App {
if let (Some((tlc_x, tlc_y)), Some((brc_x, brc_y))) =
(widget.top_left_corner, widget.bottom_right_corner)
{
if (x >= tlc_x && y >= tlc_y) && (x <= brc_x && y <= brc_y) {
if (x >= tlc_x && y >= tlc_y) && (x < brc_x && y < brc_y) {
if let Some(new_widget) = self.widget_map.get(&new_widget_id) {
self.current_widget = new_widget.clone();
@ -2939,179 +2941,187 @@ impl App {
}
// Now handle click propagation down to widget.
if let Some((_tlc_x, tlc_y)) = &self.current_widget.top_left_corner {
match &self.current_widget.widget_type {
BottomWidgetType::Proc
| BottomWidgetType::ProcSort
| BottomWidgetType::CpuLegend
| BottomWidgetType::Temp
| BottomWidgetType::Disk => {
// Get our index...
let clicked_entry = y - *tlc_y;
// + 1 so we start at 0.
let border_offset = if self.is_drawing_border() { 1 } else { 0 };
let header_gap_offset = 1 + if self.is_drawing_gap(&self.current_widget) {
self.app_config_fields.table_gap
} else {
0
};
let offset = border_offset + header_gap_offset;
if clicked_entry >= offset {
let offset_clicked_entry = clicked_entry - offset;
match &self.current_widget.widget_type {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.get_widget_state(self.current_widget.widget_id)
{
if let Some(visual_index) =
proc_widget_state.scroll_state.table_state.selected()
{
// If in tree mode, also check to see if this click is on
// the same entry as the already selected one - if it is,
// then we minimize.
if let (Some((_tlc_x, tlc_y)), Some((_brc_x, brc_y))) = (
&self.current_widget.top_left_corner,
&self.current_widget.bottom_right_corner,
) {
let border_offset = if self.is_drawing_border() { 1 } else { 0 };
let previous_scroll_position =
proc_widget_state.scroll_state.current_scroll_position;
let is_tree_mode = proc_widget_state.is_tree_mode;
let new_position = self.increment_process_position(
offset_clicked_entry as i64 - visual_index as i64,
);
if is_tree_mode {
if let Some(new_position) = new_position {
if previous_scroll_position == new_position {
self.toggle_collapsing_process_branch();
}
}
}
}
}
}
BottomWidgetType::ProcSort => {
if let Some(proc_widget_state) = self
.proc_state
.get_widget_state(self.current_widget.widget_id - 2)
{
if let Some(visual_index) =
proc_widget_state.columns.column_state.selected()
{
self.increment_process_sort_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
BottomWidgetType::CpuLegend => {
if let Some(cpu_widget_state) = self
.cpu_state
.get_widget_state(self.current_widget.widget_id - 1)
{
if let Some(visual_index) =
cpu_widget_state.scroll_state.table_state.selected()
{
self.increment_cpu_legend_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
BottomWidgetType::Temp => {
if let Some(temp_widget_state) = self
.temp_state
.get_widget_state(self.current_widget.widget_id)
{
if let Some(visual_index) =
temp_widget_state.scroll_state.table_state.selected()
{
self.increment_temp_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
BottomWidgetType::Disk => {
if let Some(disk_widget_state) = self
.disk_state
.get_widget_state(self.current_widget.widget_id)
{
if let Some(visual_index) =
disk_widget_state.scroll_state.table_state.selected()
{
self.increment_disk_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
_ => {}
}
} else {
// We might have clicked on a header! Check if we only exceeded the table + border offset, and
// it's implied we exceeded the gap offset.
if clicked_entry == border_offset {
#[allow(clippy::single_match)]
// This check ensures the click isn't actually just clicking on the bottom border.
if y < (brc_y - border_offset) {
match &self.current_widget.widget_type {
BottomWidgetType::Proc
| BottomWidgetType::ProcSort
| BottomWidgetType::CpuLegend
| BottomWidgetType::Temp
| BottomWidgetType::Disk => {
// Get our index...
let clicked_entry = y - *tlc_y;
// + 1 so we start at 0.
let header_gap_offset = 1 + if self.is_drawing_gap(&self.current_widget) {
self.app_config_fields.table_gap
} else {
0
};
let offset = border_offset + header_gap_offset;
if clicked_entry >= offset {
let offset_clicked_entry = clicked_entry - offset;
match &self.current_widget.widget_type {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.get_mut_widget_state(self.current_widget.widget_id)
.get_widget_state(self.current_widget.widget_id)
{
// Let's now check if it's a column header.
if let (Some(y_loc), Some(x_locs)) = (
&proc_widget_state.columns.column_header_y_loc,
&proc_widget_state.columns.column_header_x_locs,
) {
// debug!("x, y: {}, {}", x, y);
// debug!("y_loc: {}", y_loc);
// debug!("x_locs: {:?}", x_locs);
if let Some(visual_index) =
proc_widget_state.scroll_state.table_state.selected()
{
// If in tree mode, also check to see if this click is on
// the same entry as the already selected one - if it is,
// then we minimize.
if y == *y_loc {
for (itx, (x_left, x_right)) in
x_locs.iter().enumerate()
{
if x >= *x_left && x <= *x_right {
// Found our column!
proc_widget_state
.columns
.set_to_sorted_index_from_visual_index(
itx,
);
proc_widget_state
.update_sorting_with_columns();
self.proc_state.force_update =
Some(self.current_widget.widget_id);
break;
let previous_scroll_position = proc_widget_state
.scroll_state
.current_scroll_position;
let is_tree_mode = proc_widget_state.is_tree_mode;
let new_position = self.increment_process_position(
offset_clicked_entry as i64 - visual_index as i64,
);
if is_tree_mode {
if let Some(new_position) = new_position {
if previous_scroll_position == new_position {
self.toggle_collapsing_process_branch();
}
}
}
}
}
}
BottomWidgetType::ProcSort => {
if let Some(proc_widget_state) = self
.proc_state
.get_widget_state(self.current_widget.widget_id - 2)
{
if let Some(visual_index) =
proc_widget_state.columns.column_state.selected()
{
self.increment_process_sort_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
BottomWidgetType::CpuLegend => {
if let Some(cpu_widget_state) = self
.cpu_state
.get_widget_state(self.current_widget.widget_id - 1)
{
if let Some(visual_index) =
cpu_widget_state.scroll_state.table_state.selected()
{
self.increment_cpu_legend_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
BottomWidgetType::Temp => {
if let Some(temp_widget_state) = self
.temp_state
.get_widget_state(self.current_widget.widget_id)
{
if let Some(visual_index) =
temp_widget_state.scroll_state.table_state.selected()
{
self.increment_temp_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
BottomWidgetType::Disk => {
if let Some(disk_widget_state) = self
.disk_state
.get_widget_state(self.current_widget.widget_id)
{
if let Some(visual_index) =
disk_widget_state.scroll_state.table_state.selected()
{
self.increment_disk_position(
offset_clicked_entry as i64 - visual_index as i64,
);
}
}
}
_ => {}
}
}
}
}
BottomWidgetType::Battery => {
if let Some(battery_widget_state) = self
.battery_state
.get_mut_widget_state(self.current_widget.widget_id)
{
if let Some(tab_spacing) = &battery_widget_state.tab_click_locs {
for (itx, ((tlc_x, tlc_y), (brc_x, brc_y))) in
tab_spacing.iter().enumerate()
{
if (x >= *tlc_x && y >= *tlc_y) && (x <= *brc_x && y <= *brc_y) {
battery_widget_state.currently_selected_battery_index = itx;
break;
} else {
// We might have clicked on a header! Check if we only exceeded the table + border offset, and
// it's implied we exceeded the gap offset.
if clicked_entry == border_offset {
#[allow(clippy::single_match)]
match &self.current_widget.widget_type {
BottomWidgetType::Proc => {
if let Some(proc_widget_state) = self
.proc_state
.get_mut_widget_state(self.current_widget.widget_id)
{
// Let's now check if it's a column header.
if let (Some(y_loc), Some(x_locs)) = (
&proc_widget_state.columns.column_header_y_loc,
&proc_widget_state.columns.column_header_x_locs,
) {
// debug!("x, y: {}, {}", x, y);
// debug!("y_loc: {}", y_loc);
// debug!("x_locs: {:?}", x_locs);
if y == *y_loc {
for (itx, (x_left, x_right)) in
x_locs.iter().enumerate()
{
if x >= *x_left && x <= *x_right {
// Found our column!
proc_widget_state
.columns
.set_to_sorted_index_from_visual_index(
itx,
);
proc_widget_state
.update_sorting_with_columns();
self.proc_state.force_update =
Some(self.current_widget.widget_id);
break;
}
}
}
}
}
}
_ => {}
}
}
}
}
BottomWidgetType::Battery => {
if let Some(battery_widget_state) = self
.battery_state
.get_mut_widget_state(self.current_widget.widget_id)
{
if let Some(tab_spacing) = &battery_widget_state.tab_click_locs {
for (itx, ((tlc_x, tlc_y), (brc_x, brc_y))) in
tab_spacing.iter().enumerate()
{
if (x >= *tlc_x && y >= *tlc_y) && (x < *brc_x && y < *brc_y) {
battery_widget_state.currently_selected_battery_index = itx;
break;
}
}
}
}
}
_ => {}
}
_ => {}
}
}
}

View file

@ -61,7 +61,7 @@ impl Default for KillSignal {
pub struct AppDeleteDialogState {
pub is_showing_dd: bool,
pub selected_signal: KillSignal,
// tl x, tl y, br x, br y
/// tl x, tl y, br x, br y, index/signal
pub button_positions: Vec<(u16, u16, u16, u16, usize)>,
pub keyboard_signal_select: usize,
pub last_number_press: Option<Instant>,

View file

@ -108,20 +108,31 @@ impl KillDialog for Painter {
);
if app_state.should_get_widget_bounds() {
// This is kinda weird, but the gist is:
// - We have three sections; we put our mouse bounding box for the "yes" button at the very right edge
// of the left section and 3 characters back. We then give it a buffer size of 1 on the x-coordinate.
// - Same for the "no" button, except it is the right section and we do it from the start of the right
// section.
//
// Lastly, note that mouse detection for the dd buttons assume correct widths. As such, we correct
// them here and check with >= and <= mouse bound checks, as opposed to how we do it elsewhere with
// >= and <. See https://github.com/ClementTsang/bottom/pull/459 for details.
app_state.delete_dialog_state.button_positions = vec![
// Yes
(
button_layout[2].x,
button_layout[2].y,
button_layout[2].x + button_layout[2].width,
button_layout[2].y + button_layout[2].height,
0,
),
(
button_layout[0].x,
button_layout[0].x + button_layout[0].width - 4,
button_layout[0].y,
button_layout[0].x + button_layout[0].width,
button_layout[0].y + button_layout[0].height,
1,
button_layout[0].y,
if cfg!(target_os = "windows") { 1 } else { 15 },
),
// No
(
button_layout[2].x - 1,
button_layout[2].y,
button_layout[2].x + 2,
button_layout[2].y,
0,
),
];
}

View file

@ -136,18 +136,28 @@ impl BasicTableArrows for Painter {
);
if app_state.should_get_widget_bounds() {
// Some explanations for future readers:
// - The "height" as of writing of this entire widget is 2. If it's 1, it occasionally doesn't draw.
// - As such, the buttons are only on the lower part of this 2-high widget.
// - So, we want to only check at one location, the `draw_loc.y + 1`, and that's it.
// - But why is it "+2" then? Well, it's because I have a REALLY ugly hack
// for mouse button checking, since most button checks are of the form `(draw_loc.y + draw_loc.height)`,
// and the same for the x and width. Unfortunately, if you check using >= and <=, the outer bound is
// actually too large - so, we assume all of them are one too big and check via < (see
// https://github.com/ClementTsang/bottom/pull/459 for details).
// - So in other words, to make it simple, we keep this to a standard and overshoot by one here.
if let Some(basic_table) = &mut app_state.basic_table_widget_state {
basic_table.left_tlc =
Some((margined_draw_loc[0].x - 1, margined_draw_loc[0].y + 1));
Some((margined_draw_loc[0].x, margined_draw_loc[0].y + 1));
basic_table.left_brc = Some((
margined_draw_loc[0].x + margined_draw_loc[0].width - 1,
margined_draw_loc[0].y + 1,
margined_draw_loc[0].x + margined_draw_loc[0].width,
margined_draw_loc[0].y + 2,
));
basic_table.right_tlc =
Some((margined_draw_loc[2].x - 1, margined_draw_loc[2].y + 1));
Some((margined_draw_loc[2].x, margined_draw_loc[2].y + 1));
basic_table.right_brc = Some((
margined_draw_loc[2].x + margined_draw_loc[2].width - 1,
margined_draw_loc[2].y + 1,
margined_draw_loc[2].x + margined_draw_loc[2].width,
margined_draw_loc[2].y + 2,
));
}
}

View file

@ -198,10 +198,8 @@ impl CpuBasicWidget for Painter {
// Update draw loc in widget map
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
widget.bottom_right_corner = Some((
draw_loc.x + draw_loc.width - 1,
draw_loc.y + draw_loc.height - 1,
));
widget.bottom_right_corner =
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
}
}
}

View file

@ -122,10 +122,8 @@ impl MemBasicWidget for Painter {
if app_state.should_get_widget_bounds() {
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
widget.bottom_right_corner = Some((
draw_loc.x + draw_loc.width - 1,
draw_loc.y + draw_loc.height - 1,
));
widget.bottom_right_corner =
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
}
}
}

View file

@ -70,10 +70,8 @@ impl NetworkBasicWidget for Painter {
if app_state.should_get_widget_bounds() {
if let Some(widget) = app_state.widget_map.get_mut(&widget_id) {
widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
widget.bottom_right_corner = Some((
draw_loc.x + draw_loc.width - 1,
draw_loc.y + draw_loc.height - 1,
));
widget.bottom_right_corner =
Some((draw_loc.x + draw_loc.width, draw_loc.y + draw_loc.height));
}
}
}