Fix bug #4464: Missing autoscroll when selecting by mouse drag.

It's not very fast, but I guess that's a general problem with our scrolling. At least it works now, but it's not perfect.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@34659 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Vincent van Ravesteijn 2010-06-14 22:14:05 +00:00
parent 7f593196fc
commit 41cd987dc5
2 changed files with 81 additions and 43 deletions

View File

@ -37,6 +37,7 @@
#include "LyXVC.h" #include "LyXVC.h"
#include "qt_helpers.h" #include "qt_helpers.h"
#include "Text.h" #include "Text.h"
#include "TextMetrics.h"
#include "version.h" #include "version.h"
#include "graphics/GraphicsImage.h" #include "graphics/GraphicsImage.h"
@ -224,8 +225,7 @@ private:
// This is a 'heartbeat' generating synthetic mouse move events when the // This is a 'heartbeat' generating synthetic mouse move events when the
// cursor is at the top or bottom edge of the viewport. One scroll per 0.2 s // cursor is at the top or bottom edge of the viewport. One scroll per 0.2 s
SyntheticMouseEvent::SyntheticMouseEvent() SyntheticMouseEvent::SyntheticMouseEvent()
: timeout(200), restart_timeout(true), : timeout(200), restart_timeout(true)
x_old(-1), y_old(-1), min_scrollbar_old(-1), max_scrollbar_old(-1)
{} {}
@ -770,18 +770,19 @@ void GuiWorkArea::mouseMoveEvent(QMouseEvent * e)
e->accept(); e->accept();
// If we're above or below the work area... // If we're above or below the work area...
if (e->y() <= 20 || e->y() >= viewport()->height() - 20) { if ((e->y() <= 20 || e->y() >= viewport()->height() - 20)
&& e->buttons() == mouse_button::button1) {
// Make sure only a synthetic event can cause a page scroll, // Make sure only a synthetic event can cause a page scroll,
// so they come at a steady rate: // so they come at a steady rate:
if (e->y() <= 20) if (e->y() <= 20)
// _Force_ a scroll up: // _Force_ a scroll up:
cmd.set_y(-40); cmd.set_y(e->y() - 21);
else else
cmd.set_y(viewport()->height()); cmd.set_y(e->y() + 21);
// Store the event, to be handled when the timeout expires. // Store the event, to be handled when the timeout expires.
synthetic_mouse_event_.cmd = cmd; synthetic_mouse_event_.cmd = cmd;
if (synthetic_mouse_event_.timeout.running()) if (synthetic_mouse_event_.timeout.running()) {
// Discard the event. Note that it _may_ be handled // Discard the event. Note that it _may_ be handled
// when the timeout expires if // when the timeout expires if
// synthetic_mouse_event_.cmd has not been overwritten. // synthetic_mouse_event_.cmd has not been overwritten.
@ -790,7 +791,8 @@ void GuiWorkArea::mouseMoveEvent(QMouseEvent * e)
// occurred after the one used to start the timeout // occurred after the one used to start the timeout
// in the first place. // in the first place.
return; return;
}
synthetic_mouse_event_.restart_timeout = true; synthetic_mouse_event_.restart_timeout = true;
synthetic_mouse_event_.timeout.start(); synthetic_mouse_event_.timeout.start();
// Fall through to handle this event... // Fall through to handle this event...
@ -806,24 +808,6 @@ void GuiWorkArea::mouseMoveEvent(QMouseEvent * e)
synthetic_mouse_event_.restart_timeout = false; synthetic_mouse_event_.restart_timeout = false;
return; return;
} }
// Has anything changed on-screen since the last QMouseEvent
// was received?
if (e->x() == synthetic_mouse_event_.x_old
&& e->y() == synthetic_mouse_event_.y_old
&& synthetic_mouse_event_.min_scrollbar_old == verticalScrollBar()->minimum()
&& synthetic_mouse_event_.max_scrollbar_old == verticalScrollBar()->maximum()) {
// Nothing changed on-screen since the last QMouseEvent.
return;
}
// Yes something has changed. Store the params used to check this.
synthetic_mouse_event_.x_old = e->x();
synthetic_mouse_event_.y_old = e->y();
synthetic_mouse_event_.min_scrollbar_old = verticalScrollBar()->minimum();
synthetic_mouse_event_.max_scrollbar_old = verticalScrollBar()->maximum();
// ... and dispatch the event to the LyX core.
dispatch(cmd); dispatch(cmd);
} }
@ -863,23 +847,81 @@ void GuiWorkArea::wheelEvent(QWheelEvent * ev)
void GuiWorkArea::generateSyntheticMouseEvent() void GuiWorkArea::generateSyntheticMouseEvent()
{ {
// Set things off to generate the _next_ 'pseudo' event. int const e_y = synthetic_mouse_event_.cmd.y();
if (synthetic_mouse_event_.restart_timeout) int const wh = buffer_view_->workHeight();
synthetic_mouse_event_.timeout.start(); bool const up = e_y < 0;
bool const down = e_y > wh;
// Has anything changed on-screen since the last timeout signal // Set things off to generate the _next_ 'pseudo' event.
// was received? int step = 50;
int const min_scrollbar = verticalScrollBar()->minimum(); if (synthetic_mouse_event_.restart_timeout) {
int const max_scrollbar = verticalScrollBar()->maximum(); // This is some magic formulae to determine the speed
if (min_scrollbar == synthetic_mouse_event_.min_scrollbar_old // of scrolling related to the position of the mouse.
&& max_scrollbar == synthetic_mouse_event_.max_scrollbar_old) { int time = 200;
if (up || down) {
int dist = up ? -e_y : e_y - wh;
time = max(min(200, 250000 / (dist * dist)), 1) ;
if (time < 40) {
step = 80000 / (time * time);
time = 40;
}
}
synthetic_mouse_event_.timeout.setTimeout(time);
synthetic_mouse_event_.timeout.start();
}
// Can we scroll further ?
int const value = verticalScrollBar()->value();
if (value == verticalScrollBar()->maximum()
|| value == verticalScrollBar()->minimum()) {
synthetic_mouse_event_.timeout.stop();
return; return;
} }
// Yes it has. Store the params used to check this.
synthetic_mouse_event_.min_scrollbar_old = min_scrollbar; // Scroll
synthetic_mouse_event_.max_scrollbar_old = max_scrollbar; if (step <= 2 * wh) {
// ... and dispatch the event to the LyX core. buffer_view_->scroll(up ? -step : step);
dispatch(synthetic_mouse_event_.cmd); buffer_view_->updateMetrics();
} else {
buffer_view_->scrollDocView(value + up ? -step : step, false);
}
// In which paragraph do we have to set the cursor ?
Cursor & cur = buffer_view_->cursor();
TextMetrics const & tm = buffer_view_->textMetrics(cur.text());
pair<pit_type, const ParagraphMetrics *> p = up ? tm.first() : tm.last();
ParagraphMetrics const & pm = *p.second;
pit_type const pit = p.first;
if (pm.rows().empty())
return;
// Find the row at which we set the cursor.
RowList::const_iterator rit = pm.rows().begin();
RowList::const_iterator rlast = pm.rows().end();
int yy = pm.position() - pm.ascent();
for (--rlast; rit != rlast; ++rit) {
int h = rit->height();
if ((up && yy + h > 0)
|| (!up && yy + h > wh - defaultRowHeight()))
break;
yy += h;
}
// Find the position of the cursor
bool bound;
int x = synthetic_mouse_event_.cmd.x();
pos_type const pos = rit->pos() + tm.getColumnNearX(pit, *rit, x, bound);
// Set the cursor
cur.pit() = pit;
cur.pos() = pos;
cur.boundary(bound);
buffer_view_->buffer().changed(false);
return;
} }

View File

@ -85,10 +85,6 @@ public:
FuncRequest cmd; FuncRequest cmd;
Timeout timeout; Timeout timeout;
bool restart_timeout; bool restart_timeout;
int x_old;
int y_old;
int min_scrollbar_old;
int max_scrollbar_old;
}; };