Optimize drawing.

* BufferView:
  - update(): returns a pair of bools in order to inform for singlePar cases.
  - workAreaDispatch(): ditto and simplify the logic by using cursor update flags.

* text.C
  - breakParagraph(): change the cursor update flag as needed.
  - insertChar(): ditto.
  - erase(): ditto
  - redoParagraph(): now returns true if there is a height change.

* text3.C: dispatch():
  - initialize the cursor flag at the beginning
  - LFUN_MOUSE_PRESS: no need to update.
  - LFUN_MOUSE_RELEASE: ditto.
  - add some FIXMEs and comments.

* LyXFunc::dispatch(): simplify the BufferView update.

* insets/insetcollapsable.C: clarify and simplify the logic in doDispatch().

* WorkArea::dispatch(): redraw only if needed.



git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@15958 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Abdelrazak Younes 2006-11-17 17:42:52 +00:00
parent 3bc4d28449
commit c66d2f1ee5
8 changed files with 127 additions and 74 deletions

View File

@ -339,7 +339,7 @@ bool BufferView::multiParSel()
}
bool BufferView::update(Update::flags flags)
std::pair<bool, bool> BufferView::update(Update::flags flags)
{
// This is close to a hot-path.
if (lyxerr.debugging(Debug::DEBUG)) {
@ -353,7 +353,7 @@ bool BufferView::update(Update::flags flags)
// Check needed to survive LyX startup
if (!buffer_)
return false;
return make_pair(false, false);
if (lyxerr.debugging(Debug::WORKAREA)) {
lyxerr[Debug::WORKAREA] << "BufferView::update" << std::endl;
@ -362,15 +362,31 @@ bool BufferView::update(Update::flags flags)
// Update macro store
buffer_->buildMacros();
// First drawing step
updateMetrics(flags & Update::SinglePar);
// Now do the first drawing step if needed. This consists on updating
// the CoordCache in updateMetrics().
// The second drawing step is done in WorkArea::redraw() if needed.
bool const need_second_step =
(flags & (Update::SinglePar | Update::Force | Update::FitCursor | Update::MultiParSel))
&& (fitCursor() || multiParSel());
return need_second_step;
// Case when no explicit update is requested.
if (!(flags & (Update::SinglePar | Update::Force))) {
if (fitCursor() || multiParSel()) {
// a CoordCache update is needed
updateMetrics(false);
// tell the frontend to update the screen.
return make_pair(true, false);
}
// no need to do anything.
return make_pair(false, false);
}
// We are now in the case (Update::SinglePar | Update::Force)
bool single_par = flags & Update::SinglePar;
updateMetrics(single_par);
// Don't forget to do check for fitCursor() and multiParSel().
fitCursor();
multiParSel();
return make_pair(true, single_par);
}
@ -1006,7 +1022,7 @@ void BufferView::workAreaResize(int width, int height)
}
bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
std::pair<bool, bool> BufferView::workAreaDispatch(FuncRequest const & cmd0)
{
//lyxerr << BOOST_CURRENT_FUNCTION << "[ cmd0 " << cmd0 << "]" << endl;
@ -1016,14 +1032,11 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
// E.g. Qt mouse press when no buffer
if (!buffer_)
return false;
bool needRedraw = false;
return make_pair(false, false);
LCursor cur(*this);
cur.push(buffer_->inset());
cur.selection() = cursor_.selection();
needRedraw |= cur.selection();
// Either the inset under the cursor or the
// surrounding LyXText will handle this event.
@ -1046,7 +1059,6 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
// cur.bv().cursor() = cur; (or similar)
if (inset) {
inset->dispatch(cur, cmd);
needRedraw = true;
}
// Now dispatch to the temporary cursor. If the real cursor should
@ -1054,17 +1066,12 @@ bool BufferView::workAreaDispatch(FuncRequest const & cmd0)
if (!cur.result().dispatched())
cur.dispatch(cmd);
if (cur.result().dispatched()) {
// Redraw if requested or necessary.
if (cur.result().update())
needRedraw |= update(Update::FitCursor | Update::Force);
else
needRedraw |= update(Update::FitCursor | Update::MultiParSel);
}
// Redraw if requested and necessary.
if (cur.result().dispatched() && cur.result().update())
return update(cur.result().update());
// When the above and the inner function are fixed, we can do this:
//return needRedraw;
return true;
return make_pair(false, false);
}
@ -1217,7 +1224,7 @@ ViewMetricsInfo const & BufferView::viewMetricsInfo()
void BufferView::updateMetrics(bool singlepar)
{
// Clear out the position cache in case of full screen redraw.
//if (!singlepar)
if (!singlepar)
coord_cache_.clear();
LyXText & buftext = buffer_->text();

View File

@ -24,6 +24,7 @@
#include <boost/utility.hpp>
#include <boost/signal.hpp>
#include <utility>
#include <string>
@ -97,9 +98,11 @@ public:
* to do a fitcursor, and to force an update if screen
* position changes. \c forceupdate means to force an update
* in any case.
* \return true if a full updateMetrics() is needed.
* \retval (false, xxx) if no redraw is required
* \retval (true, true) if a single paragraph redraw is needed
* \retval (true, false) if a full redraw is needed
*/
bool update(Update::flags flags = Update::FitCursor | Update::Force);
std::pair<bool, bool> update(Update::flags flags = Update::FitCursor | Update::Force);
/// move the screen to fit the cursor.
/// Only to be called with good y coordinates (after a bv::metrics)
@ -167,8 +170,10 @@ public:
/// dispatch method helper for \c WorkArea
/// \sa WorkArea
/// \return true if a full redraw is needed
bool workAreaDispatch(FuncRequest const & ev);
/// \retval (false, xxx) if no redraw is required
/// \retval (true, true) if a single paragraph redraw is needed
/// \retval (true, false) if a full redraw is needed
std::pair<bool, bool> workAreaDispatch(FuncRequest const & ev);
/// access to anchor.
pit_type anchor_ref() const;

View File

@ -191,7 +191,7 @@ void WorkArea::dispatch(FuncRequest const & cmd0)
theLyXFunc().setLyXView(&lyx_view_);
bool needRedraw = buffer_view_->workAreaDispatch(cmd0);
std::pair<bool, bool> needRedraw = buffer_view_->workAreaDispatch(cmd0);
// Skip these when selecting
if (cmd0.action != LFUN_MOUSE_MOTION) {
@ -209,8 +209,8 @@ void WorkArea::dispatch(FuncRequest const & cmd0)
hideCursor();
toggleCursor();
if (needRedraw)
redraw();
if (needRedraw.first)
redraw(needRedraw.second);
}

View File

@ -293,6 +293,11 @@ void InsetCollapsable::doDispatch(LCursor & cur, FuncRequest & cmd)
switch (cmd.action) {
case LFUN_MOUSE_PRESS:
if (cmd.button() == mouse_button::button1 && hitButton(cmd)) {
cur.dispatched();
cur.noUpdate();
break;
}
if (status() == Inlined)
InsetText::doDispatch(cur, cmd);
else if (status() == Open && !hitButton(cmd))
@ -314,36 +319,35 @@ void InsetCollapsable::doDispatch(LCursor & cur, FuncRequest & cmd)
case LFUN_MOUSE_RELEASE:
if (cmd.button() == mouse_button::button3) {
// Open the Inset configuration dialog
showInsetDialog(&cur.bv());
break;
}
switch (status()) {
case Collapsed:
//lyxerr << "InsetCollapsable::lfunMouseRelease 1" << endl;
setStatus(cur, Open);
edit(cur, true);
cur.bv().cursor() = cur;
break;
case Open: {
if (hitButton(cmd)) {
//lyxerr << "InsetCollapsable::lfunMouseRelease 2" << endl;
setStatus(cur, Collapsed);
cur.bv().cursor() = cur;
} else {
//lyxerr << "InsetCollapsable::lfunMouseRelease 3" << endl;
InsetText::doDispatch(cur, cmd);
}
break;
}
case Inlined:
//lyxerr << "InsetCollapsable::lfunMouseRelease 4" << endl;
if (status() == Inlined) {
// The mouse click has to be within the inset!
InsetText::doDispatch(cur, cmd);
break;
}
if (cmd.button() == mouse_button::button1 && hitButton(cmd)) {
// Left button is clicked, the user asks to toggle the inset
// visual state.
cur.dispatched();
cur.updateFlags(Update::Force | Update::FitCursor);
if (status() == Collapsed) {
setStatus(cur, Open);
edit(cur, true);
}
else {
setStatus(cur, Collapsed);
}
cur.bv().cursor() = cur;
break;
}
// The mouse click is within the opened inset.
InsetText::doDispatch(cur, cmd);
break;
case LFUN_INSET_TOGGLE:

View File

@ -1715,15 +1715,11 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
// Redraw screen unless explicitly told otherwise.
// This also initializes the position cache for all insets
// in (at least partially) visible top-level paragraphs.
bool needSecondUpdate = false;
if (updateFlags != Update::None)
needSecondUpdate = view()->update(updateFlags);
else
needSecondUpdate = view()->fitCursor();
std::pair<bool, bool> needSecondUpdate = view()->update(updateFlags);
if (needSecondUpdate.first)
view()->buffer()->changed(needSecondUpdate.second);
if (needSecondUpdate || updateFlags != Update::None) {
view()->buffer()->changed(updateFlags & Update::SinglePar);
}
lyx_view_->updateStatusBar();
// if we executed a mutating lfun, mark the buffer as dirty

View File

@ -96,7 +96,9 @@ public:
/// Set font over selection paragraphs and rebreak.
void setFont(LCursor & cur, LyXFont const &, bool toggleall = false);
/// rebreaks the given par
/// Rebreaks the given paragraph.
/// \retval true if a full screen redraw is needed.
/// \retval false if a single paragraph redraw is enough.
bool redoParagraph(BufferView &, pit_type pit);
/// returns pos in given par at given x coord

View File

@ -1153,8 +1153,12 @@ void LyXText::breakParagraph(LCursor & cur, bool keep_layout)
// Because of the mix between the model (the paragraph contents) and the
// view (the paragraph breaking in rows, we have to do this here before
// the setCursor() call below.
redoParagraph(cur.bv(), cpit);
redoParagraph(cur.bv(), cpit + 1);
bool changed_height = redoParagraph(cur.bv(), cpit);
changed_height |= redoParagraph(cur.bv(), cpit + 1);
if (changed_height)
// A singlePar update is not enough in this case.
cur.updateFlags(Update::Force);
// This check is necessary. Otherwise the new empty paragraph will
// be deleted automatically. And it is more friendly for the user!
@ -1253,7 +1257,9 @@ void LyXText::insertChar(LCursor & cur, char_type c)
// FIXME: Inserting a character has nothing to do with setting a cursor.
// Because of the mix between the model (the paragraph contents) and the
// view (the paragraph breaking in rows, we have to do this here.
redoParagraph(cur.bv(), cur.pit());
if (redoParagraph(cur.bv(), cur.pit()))
// A singlePar update is not enough in this case.
cur.updateFlags(Update::Force);
setCursor(cur, cur.pit(), cur.pos() + 1, false, cur.boundary());
charInserted();
}
@ -1671,13 +1677,16 @@ bool LyXText::erase(LCursor & cur)
} else
needsUpdate = dissolveInset(cur);
// Make sure the cursor is correct. Is this really needed?
// FIXME: Inserting characters has nothing to do with setting a cursor.
// Because of the mix between the model (the paragraph contents)
// and the view (the paragraph breaking in rows, we have to do this
// here before the setCursorIntern() call.
if (needsUpdate) {
redoParagraph(cur.bv(), cur.pit());
if (redoParagraph(cur.bv(), cur.pit()))
// A singlePar update is not enough in this case.
cur.updateFlags(Update::Force);
// Make sure the cursor is correct. Is this really needed?
// No, not really... at least not here!
setCursorIntern(cur, cur.pit(), cur.pos());
}
@ -1786,7 +1795,9 @@ bool LyXText::backspace(LCursor & cur)
// Because of the mix between the model (the paragraph contents)
// and the view (the paragraph breaking in rows, we have to do this
// here before the setCursor() call.
redoParagraph(cur.bv(), cur.pit());
if (redoParagraph(cur.bv(), cur.pit()))
// A singlePar update is not enough in this case.
cur.updateFlags(Update::Force);
setCursor(cur, cur.pit(), cur.pos(), false, cur.boundary());
return needsUpdate;
@ -1929,7 +1940,7 @@ bool LyXText::redoParagraph(BufferView & bv, pit_type const pit)
dim.asc += par.rows()[0].ascent();
dim.des -= par.rows()[0].ascent();
bool const same = dim == par.dim();
bool const same = dim.height() == par.dim().height();
par.dim() = dim;
//lyxerr << "redoParagraph: " << par.rows().size() << " rows\n";

View File

@ -305,6 +305,11 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
{
lyxerr[Debug::ACTION] << "LyXText::dispatch: cmd: " << cmd << endl;
// FIXME: We use the update flag to indicates wether a singlePar or a
// full screen update is needed. We reset it here but shall we restore it
// at the end?
cur.noUpdate();
BOOST_ASSERT(cur.text() == this);
BufferView * bv = &cur.bv();
CursorSlice oldTopSlice = cur.top();
@ -988,6 +993,11 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
lyx::dispatch(FuncRequest(LFUN_PRIMARY_SELECTION_PASTE, "paragraph"));
}
if (cmd.button() == mouse_button::button1) {
needsUpdate = false;
cur.noUpdate();
}
break;
}
@ -1037,8 +1047,12 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
break;
// finish selection
if (cmd.button() == mouse_button::button1)
theSelection().haveSelection(cur.selection());
if (cmd.button() == mouse_button::button1) {
if (cur.selection())
theSelection().haveSelection(cur.selection());
needsUpdate = false;
cur.noUpdate();
}
bv->switchKeyMap();
break;
@ -1474,6 +1488,19 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
}
needsUpdate |= (cur.pos() != cur.lastpos()) && cur.selection();
// FIXME: The cursor flag is reset two lines below
// so we need to check here if some of the LFUN did touch that.
// for now only LyXText::erase() and LyXText::backspace() do that.
// The plan is to verify all the LFUNs and then to remove this
// singleParUpdate boolean altogether.
if (cur.result().update() & Update::Force) {
singleParUpdate = false;
needsUpdate = true;
}
// FIXME: the following code should go in favor of fine grained
// update flag treatment.
if (singleParUpdate)
// Inserting characters does not change par height
if (cur.bottom().paragraph().dim().height()
@ -1485,10 +1512,11 @@ void LyXText::dispatch(LCursor & cur, FuncRequest & cmd)
return;
} else
needsUpdate = true;
if (!needsUpdate
&& &oldTopSlice.inset() == &cur.inset()
&& oldTopSlice.idx() == cur.idx()
&& !sel
&& !sel // sel is a backup of cur.selection() at the biginning of the function.
&& !cur.selection())
cur.noUpdate();
else