Merge pull request #251 from x-qq/fix_scrollbar_segfaults

fix for scrollbar segfaults

* fixes #206
* fixes broken mousewheel scrolling of the scenedit palette
* removed boost threads dependency
* added foundation for further refactoring of the drawing and event handling code: interfaces and drawable manager with layering
* removed a bunch of unneeded redraw calls
* removed some repeated recalculation of effectively constant values (boe.actions)
* removed recalculation of effectively constant scrollbar and button positions (boe.graphics)

Closes #251
This commit is contained in:
2020-02-09 15:55:51 -05:00
parent f141149287
commit deac7b0cb6
27 changed files with 444 additions and 219 deletions

View File

@@ -62,6 +62,10 @@ void cScrollbar::setStyle(eScrollStyle newStyle) {
style = newStyle;
}
void cScrollbar::set_wheel_event_rect(rectangle rect) {
this->wheel_event_rect = rect;
}
long cScrollbar::getPosition() {
return pos;
}
@@ -86,6 +90,190 @@ eScrollStyle cScrollbar::getStyle() {
return style;
}
// TODO: centralize this translation somewhere
// Translate raw x/y position using the view of the current rendering target
location cScrollbar::translated_location(sf::Vector2i const point) const {
return location { this->inWindow->mapPixelToCoords(point) };
}
bool cScrollbar::handle_event(sf::Event const & event) {
// Not visible -> not interested
if(!this->isVisible())
return false;
// Visible but no maximum -> not interested
if(this->getMaximum() == 0) {
return false;
}
switch(event.type) {
case sf::Event::MouseButtonPressed:
return this->handle_mouse_pressed(event);
case sf::Event::MouseMoved:
return this->handle_mouse_moved(event);
case sf::Event::MouseButtonReleased:
return this->handle_mouse_released(event);
case sf::Event::MouseWheelScrolled:
return this->handle_mouse_wheel_scrolled(event);
default: break;
}
return false;
}
bool cScrollbar::handle_mouse_wheel_scrolled(sf::Event const & event) {
location event_location = this->translated_location({
event.mouseWheelScroll.x,
event.mouseWheelScroll.y
});
// Scrolling outside of catching area or own frame -> not interested.
if(!(event_location.in(this->wheel_event_rect) || event_location.in(this->getBounds())))
return false;
this->setPosition(this->getPosition() - event.mouseWheelScroll.delta);
return true;
}
// Given a (translated) location, determine which part of the scrollbar it corresponds to
auto cScrollbar::location_to_part(location const & location) const -> eScrollbarPart {
cScrollbar::eScrollbarPart part;
// Yes, this is a mess, but at least it's relatively small.
int clickPos = this->vert ? location.y : location.x;
int bar_start = this->vert ? this->frame.top : this->frame.left;
int bar_end = this->vert ? this->frame.bottom : this->frame.right;
int total_bar_size = this->vert ? this->frame.height() : this->frame.width();
int btn_size = this->vert
? this->up_rect[this->style][cScrollbar::VERT].height()
: this->up_rect[this->style][cScrollbar::HORZ].width();
int bar_size = total_bar_size - btn_size * 2;
int thumbPos = bar_start + btn_size + this->pos * (bar_size - btn_size) / this->max;
if(clickPos < bar_start + btn_size) {
part = cScrollbar::PART_UP;
} else if(clickPos < thumbPos) {
part = cScrollbar::PART_PGUP;
} else if(clickPos < thumbPos + btn_size) {
part = cScrollbar::PART_THUMB;
} else if(clickPos < bar_end - btn_size) {
part = cScrollbar::PART_PGDN;
} else {
part = cScrollbar::PART_DOWN;
}
return part;
}
bool cScrollbar::handle_mouse_pressed(sf::Event const & event) {
location event_location = this->translated_location({
event.mouseButton.x,
event.mouseButton.y
});
// Mouse pressed somewhere outside -> not interested
if(!event_location.in(this->getBounds())) return false;
// NOTE: depressed actually means pressed
this->depressed = true;
this->pressedPart = this->location_to_part(event_location);
// If the thumb is being dragged, record the initial click location
if(this->pressedPart == cScrollbar::eScrollbarPart::PART_THUMB) {
this->mouse_pressed_at = event_location;
this->drag_start_position = this->pos;
}
return true;
}
bool cScrollbar::handle_mouse_moved(sf::Event const & event) {
// Mouse movements while not pressed -> not interested
// NOTE: depressed actually means pressed
if(!this->depressed) return false;
// is there a need for restore_cursor() anywhere around here, and why?
location event_location = this->translated_location({
event.mouseMove.x,
event.mouseMove.y
});
if(this->pressedPart == cScrollbar::eScrollbarPart::PART_THUMB) {
// Thumb being dragged.
this->handle_thumb_drag(event_location);
return true;
} else {
// Dragging something ... but not thumb
if(!event_location.in(this->getBounds())) {
// Mouse was moved out of the scrollbar and not dragging thumb
// NOTE: depressed actually means pressed
this->depressed = false;
// The event is outside the scrollbar so someone else might want to consume this.
return false;
}
}
// Dragging something, but not thumb, but still within scrollbar bounds. Okay.jpg.
return true;
}
void cScrollbar::handle_thumb_drag(location const & event_location) {
int bar_start = this->vert ? this->frame.top : this->frame.left;
int bar_end = this->vert ? this->frame.bottom : this->frame.right;
int btn_size = this->vert
? this->up_rect[this->style][cScrollbar::VERT].height()
: this->up_rect[this->style][cScrollbar::HORZ].width();
// XXX A lot of this looks like it can be precalculated once, but
// be careful with the integer rounding.
int thumb_min_position = bar_start + btn_size;
int thumb_max_position = bar_end - 2 * btn_size; // <- two button sizes: one for the top/left arrow and one for the thumb
int start = this->vert ? this->mouse_pressed_at.y : this->mouse_pressed_at.x;
int current = this->vert ? event_location.y : event_location.x;
// This is done in this particular way to minimize integer rounding
// that would otherwise heavily impact the result (when max is
// significantly large compared to the pixel size of the scrollbar).
int diff_in_steps = (current - start) * this->max / (thumb_max_position - thumb_min_position);
this->setPosition(this->drag_start_position + diff_in_steps);
}
bool cScrollbar::handle_mouse_released(sf::Event const & event) {
// Mouse released while not pressed -> not interested
// NOTE: depressed actually means pressed
if(!this->depressed) return false;
switch(this->pressedPart) {
case cScrollbar::PART_UP: this->pos--; break;
case cScrollbar::PART_DOWN: this->pos++; break;
case cScrollbar::PART_PGUP: this->pos -= this->pgsz; break;
case cScrollbar::PART_PGDN: this->pos += this->pgsz; break;
case cScrollbar::PART_THUMB: break;
default: break;
}
// Normalize
this->pos = minmax(0, this->max, this->pos);
// NOTE: depressed actually means pressed
this->depressed = false;
return true;
}
bool cScrollbar::handleClick(location where) {
if(max == 0) return false;
sf::Event e;

View File

@@ -13,6 +13,8 @@
/// Scrollbar-related classes and types.
#include "control.hpp"
#include "event_listener.hpp"
#include "drawable.hpp"
/// Specifies the style of a scrollbar.
enum eScrollStyle {
@@ -24,7 +26,7 @@ enum eScrollStyle {
/// This has no coupling with scrollable data; that must be handled externally by
/// using the methods to get the scrollbar's position.
/// Alternatively, it can be used as a slider control.
class cScrollbar : public cControl {
class cScrollbar : public cControl, public iEventListener, public iDrawable {
int pos, max, pgsz;
std::string link;
// Make sure this is equal to the number of constants in eScrollStyle
@@ -34,7 +36,7 @@ class cScrollbar : public cControl {
VERT, VERT_PRESSED, HORZ, HORZ_PRESSED
};
// Note: For horizontal scrollbars, up is left and down is right.
enum {
enum eScrollbarPart {
PART_UP,
PART_PGUP,
PART_THUMB,
@@ -45,7 +47,20 @@ class cScrollbar : public cControl {
bool vert = true;
static std::string scroll_textures[NUM_STYLES];
static const rectangle up_rect[NUM_STYLES][4], down_rect[NUM_STYLES][4], bar_rect[NUM_STYLES][4], thumb_rect[NUM_STYLES][4];
// Mouse wheel scrolling events inside this rectangle will be handled by the scrollbar.
// Should at least cover the scrollbar itself, but can extend outside (example: scrolling
// in the inventory area).
rectangle wheel_event_rect {0, 0, 0, 0};
void draw_vertical(), draw_horizontal();
location translated_location(sf::Vector2i const) const;
eScrollbarPart location_to_part(location const & location) const;
location mouse_pressed_at;
int drag_start_position;
bool handle_mouse_pressed(sf::Event const &);
bool handle_mouse_moved(sf::Event const &);
bool handle_mouse_released(sf::Event const &);
bool handle_mouse_wheel_scrolled(sf::Event const &);
void handle_thumb_drag(location const &);
public:
/// @copydoc cDialog::init()
static void init();
@@ -114,7 +129,9 @@ public:
/// Set the scrollbar style.
/// @param newStyle The new style.
void setStyle(eScrollStyle newStyle);
void draw();
void set_wheel_event_rect(rectangle);
virtual void draw() override;
virtual bool handle_event(sf::Event const &) override;
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers