mirror of
https://git.lyx.org/repos/lyx.git
synced 2024-12-22 13:18:28 +00:00
Merge remote-tracking branch 'features/properpaint'
Now painting the workarea is done at paint events as should be. Explicit painting after updating metrics has been replaced by a much lighter procedure (updatePosCache) to update the insets positions cache. Expected benefits: - better performance - proper use of subpixel aliasing The LyXRC variable use_qimage is not needed anymore and is therefore removed.
This commit is contained in:
commit
3d590d3bd7
@ -67,31 +67,6 @@ drawing from metrics.
|
||||
Other changes are only clean-ups.
|
||||
|
||||
** When a paragraph ends with a newline, compute correctly the height of the extra row.
|
||||
** Rewrite TextMetrics::editXY, checkInsetHit using row information (getPosNearX)?
|
||||
|
||||
The helper version should return a Row::Element instead of an InsetTable.
|
||||
|
||||
** Set inset position during metrics phase
|
||||
|
||||
In order to do that, a no-paint drawing will be initiated after every
|
||||
redoParagraph. This code path will need to be made as fast as possible.
|
||||
|
||||
Effect: avoid depending on actual drawing having taken place. In turn,
|
||||
it will allow to do drawing on paint events, like any reasonable
|
||||
application would do.
|
||||
|
||||
** Cleanup after complete metrics
|
||||
Then the following can be done:
|
||||
+ remove hack in InsetMathNest::drawSelection
|
||||
+ remove painting when not inside in drawParagraph
|
||||
+ remove Cursor::inCoordCache?
|
||||
|
||||
** Paint directly to screen
|
||||
|
||||
Instead of using an intermediary pixmap. I have no idea of how
|
||||
difficult it will prove.
|
||||
One benefit will be that subpixel aliasing will work again (#9972)
|
||||
|
||||
** Merging bv::updateMetrics and tm::metrics
|
||||
|
||||
While the full metrics computation tries hard to limit the number of
|
||||
@ -107,19 +82,19 @@ necessary to break the whole contents to know the width of the cell.
|
||||
|
||||
* Description of current drawing mechanism
|
||||
|
||||
** Two stage drawing
|
||||
** Three-stage drawing
|
||||
|
||||
There are two parts to drawing the work area:
|
||||
There are three parts to drawing the work area:
|
||||
|
||||
+ the metrics phase computes the size of insets and breaks the
|
||||
paragraphs into rows. It stores the dimension of insets (both
|
||||
normal and math) in bv::coordCache.
|
||||
|
||||
+ the drawing phase draws the contents and caches the inset
|
||||
positions. Since the caching of positions is useful in itself,
|
||||
there is a provision for drawing "without" drawing when the only
|
||||
thing we want is to cache inset positions
|
||||
(Painter::setDrawingEnabled).
|
||||
+ the nodraw drawing phase paints the screen (see below) with a null
|
||||
painter. The only useful effect is to store the inset positions.
|
||||
|
||||
+ an update() signal is sent. This in turn will trigger a paint
|
||||
event, and the actual screen painting will happen then.
|
||||
|
||||
The machinery is controlled via bv::processUpdateFlags. This method is
|
||||
called at the end of bv::mouseEventDispatch and in
|
||||
@ -145,7 +120,7 @@ update flag is Update::None.
|
||||
** Metrics computation
|
||||
|
||||
This is triggered by bv::updateMetrics, which calls tm::redoParagraph for
|
||||
all visible paragraphs. Paragraphs above or below the screen (needed
|
||||
all visible paragraphs. Some Paragraphs above or below the screen (needed
|
||||
for page up/down) and computed as needed.
|
||||
|
||||
tm::redoParagraph will call Inset::metrics for each inset. In the case
|
||||
@ -155,8 +130,9 @@ all the paragraphs of the inset.
|
||||
|
||||
** Drawing the work area.
|
||||
|
||||
This is done in bv::draw. This method is triggered mainly by
|
||||
Buffer::changed, which draws all the work areas that show the given buffer.
|
||||
This is done in bv::draw. This method is triggered by a paint event,
|
||||
mainly called through Buffer::changed, which draws all the work areas
|
||||
that show the given buffer.
|
||||
|
||||
Note that, When Buffer::changed is called outside of
|
||||
bv::processUpdateFlags, it is not clear whether the update strategy
|
||||
@ -186,3 +162,6 @@ The action depends on the update strategy:
|
||||
|
||||
+ SingleParUpdate: only tries to repaint current paragraph in a way
|
||||
that is not yet very clear to me.
|
||||
|
||||
BufferView::draw can also be called with a null painter from
|
||||
BufferView::updateMetrics().
|
||||
|
@ -1772,7 +1772,7 @@ if __name__ == '__main__':
|
||||
lyx_check_config = True
|
||||
lyx_kpsewhich = True
|
||||
outfile = 'lyxrc.defaults'
|
||||
lyxrc_fileformat = 22
|
||||
lyxrc_fileformat = 23
|
||||
rc_entries = ''
|
||||
lyx_keep_temps = False
|
||||
version_suffix = ''
|
||||
|
@ -94,6 +94,9 @@
|
||||
# Add pygmentize_command for the python pygments syntax highlighter
|
||||
# No conversion necessary.
|
||||
|
||||
# Incremented to format 23, by lasgouttes
|
||||
# Remove use_qimage preference
|
||||
|
||||
# NOTE: The format should also be updated in LYXRC.cpp and
|
||||
# in configure.py.
|
||||
|
||||
@ -364,6 +367,20 @@ def remove_print_support(line):
|
||||
# End conversions for LyX 2.1 to 2.2
|
||||
####################################
|
||||
|
||||
|
||||
#################################
|
||||
# Conversions from LyX 2.3 to 2.4
|
||||
|
||||
def remove_use_qimage(line):
|
||||
if not line.lower().startswith("\\use_qimage "):
|
||||
return no_match
|
||||
return (True, "")
|
||||
|
||||
# End conversions for LyX 2.3 to 2.4
|
||||
####################################
|
||||
|
||||
|
||||
|
||||
conversions = [
|
||||
[ 1, [ # there were several conversions for format 1
|
||||
export_menu,
|
||||
@ -392,5 +409,6 @@ conversions = [
|
||||
[ 19, [remove_print_support]],
|
||||
[ 20, []],
|
||||
[ 21, []],
|
||||
[ 22, []]
|
||||
[ 22, []],
|
||||
[ 23, [remove_use_qimage]]
|
||||
]
|
||||
|
@ -70,6 +70,7 @@
|
||||
#include "frontends/Application.h"
|
||||
#include "frontends/Delegates.h"
|
||||
#include "frontends/FontMetrics.h"
|
||||
#include "frontends/NullPainter.h"
|
||||
#include "frontends/Painter.h"
|
||||
#include "frontends/Selection.h"
|
||||
|
||||
@ -234,7 +235,7 @@ struct BufferView::Private
|
||||
last_inset_(0), clickable_inset_(false),
|
||||
mouse_position_cache_(),
|
||||
bookmark_edit_position_(-1), gui_(0),
|
||||
horiz_scroll_offset_(0)
|
||||
horiz_scroll_offset_(0), repaint_caret_row_(false)
|
||||
{
|
||||
xsel_cache_.set = false;
|
||||
}
|
||||
@ -311,6 +312,12 @@ struct BufferView::Private
|
||||
/// a slice pointing to the start of the row where cursor was
|
||||
/// at previous draw event
|
||||
CursorSlice last_row_slice_;
|
||||
|
||||
/// a slice pointing to where the cursor has been drawn after the current
|
||||
/// draw() call.
|
||||
CursorSlice caret_slice_;
|
||||
/// indicates whether the caret slice needs to be repainted in this draw() run.
|
||||
bool repaint_caret_row_;
|
||||
};
|
||||
|
||||
|
||||
@ -2651,6 +2658,7 @@ bool BufferView::singleParUpdate()
|
||||
// the singlePar optimisation.
|
||||
return false;
|
||||
|
||||
tm.updatePosCache(bottom_pit);
|
||||
d->update_strategy_ = SingleParUpdate;
|
||||
|
||||
LYXERR(Debug::PAINTING, "\ny1: " << pm.position() - pm.ascent()
|
||||
@ -2704,6 +2712,7 @@ void BufferView::updateMetrics()
|
||||
// in the first line.
|
||||
}
|
||||
anchor_pm.setPosition(d->anchor_ypos_);
|
||||
tm.updatePosCache(d->anchor_pit_);
|
||||
|
||||
LYXERR(Debug::PAINTING, "metrics: "
|
||||
<< " anchor pit = " << d->anchor_pit_
|
||||
@ -2719,6 +2728,7 @@ void BufferView::updateMetrics()
|
||||
y1 -= pm.descent();
|
||||
// Save the paragraph position in the cache.
|
||||
pm.setPosition(y1);
|
||||
tm.updatePosCache(pit1);
|
||||
y1 -= pm.ascent();
|
||||
}
|
||||
|
||||
@ -2732,6 +2742,7 @@ void BufferView::updateMetrics()
|
||||
y2 += pm.ascent();
|
||||
// Save the paragraph position in the cache.
|
||||
pm.setPosition(y2);
|
||||
tm.updatePosCache(pit2);
|
||||
y2 += pm.descent();
|
||||
}
|
||||
|
||||
@ -2745,6 +2756,9 @@ void BufferView::updateMetrics()
|
||||
|
||||
d->update_strategy_ = FullScreenUpdate;
|
||||
|
||||
// Now update the positions of insets in the cache.
|
||||
updatePosCache();
|
||||
|
||||
if (lyxerr.debugging(Debug::WORKAREA)) {
|
||||
LYXERR(Debug::WORKAREA, "BufferView::updateMetrics");
|
||||
d->coord_cache_.dump();
|
||||
@ -2752,6 +2766,15 @@ void BufferView::updateMetrics()
|
||||
}
|
||||
|
||||
|
||||
void BufferView::updatePosCache()
|
||||
{
|
||||
// this is the "nodraw" drawing stage: only set the positions of the
|
||||
// insets in metrics cache.
|
||||
frontend::NullPainter np;
|
||||
draw(np, false);
|
||||
}
|
||||
|
||||
|
||||
void BufferView::insertLyXFile(FileName const & fname)
|
||||
{
|
||||
LASSERT(d->cursor_.inTexted(), return);
|
||||
@ -2886,7 +2909,7 @@ bool BufferView::paragraphVisible(DocIterator const & dit) const
|
||||
}
|
||||
|
||||
|
||||
void BufferView::cursorPosAndHeight(Point & p, int & h) const
|
||||
void BufferView::caretPosAndHeight(Point & p, int & h) const
|
||||
{
|
||||
Cursor const & cur = cursor();
|
||||
Font const font = cur.real_current_font;
|
||||
@ -2959,7 +2982,24 @@ void BufferView::setCurrentRowSlice(CursorSlice const & rowSlice)
|
||||
}
|
||||
|
||||
|
||||
void BufferView::checkCursorScrollOffset(PainterInfo & pi)
|
||||
namespace {
|
||||
|
||||
bool sliceInRow(CursorSlice const & cs, Text const * text, Row const & row)
|
||||
{
|
||||
return !cs.empty() && cs.text() == text && cs.pit() == row.pit()
|
||||
&& row.pos() <= cs.pos() && cs.pos() <= row.endpos();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool BufferView::needRepaint(Text const * text, Row const & row) const
|
||||
{
|
||||
return d->repaint_caret_row_ && sliceInRow(d->caret_slice_, text, row);
|
||||
}
|
||||
|
||||
|
||||
void BufferView::checkCursorScrollOffset()
|
||||
{
|
||||
CursorSlice rowSlice = d->cursor_.bottom();
|
||||
TextMetrics const & tm = textMetrics(rowSlice.text());
|
||||
@ -2976,35 +3016,6 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
|
||||
// Set the row on which the cursor lives.
|
||||
setCurrentRowSlice(rowSlice);
|
||||
|
||||
// If insets referred to by cursor are not all in the cache, the positions
|
||||
// need to be recomputed.
|
||||
if (!d->cursor_.inCoordCache()) {
|
||||
/** FIXME: the code below adds an extraneous computation of
|
||||
* inset positions, and can therefore be bad for performance
|
||||
* (think for example about a very large tabular inset.
|
||||
* Redawing the row where it is means redrawing the whole
|
||||
* screen).
|
||||
*
|
||||
* The bug that this fixes is the following: assume that there
|
||||
* is a very large math inset. Upon entering the inset, when
|
||||
* pressing `End', the row is not scrolled and the cursor is
|
||||
* not visible. The extra row computation makes sure that the
|
||||
* inset positions are correctly computed and set in the
|
||||
* cache. This would not happen if we did not have two-stage
|
||||
* drawing.
|
||||
*
|
||||
* A proper fix would be to always have proper inset positions
|
||||
* at this point.
|
||||
*/
|
||||
// Force the recomputation of inset positions
|
||||
bool const drawing = pi.pain.isDrawingEnabled();
|
||||
pi.pain.setDrawingEnabled(false);
|
||||
// No need to care about vertical position.
|
||||
RowPainter rp(pi, buffer().text(), row, -d->horiz_scroll_offset_, 0);
|
||||
rp.paintText();
|
||||
pi.pain.setDrawingEnabled(drawing);
|
||||
}
|
||||
|
||||
// Current x position of the cursor in pixels
|
||||
int cur_x = getPos(d->cursor_).x_;
|
||||
|
||||
@ -3048,7 +3059,7 @@ void BufferView::checkCursorScrollOffset(PainterInfo & pi)
|
||||
}
|
||||
|
||||
|
||||
void BufferView::draw(frontend::Painter & pain)
|
||||
void BufferView::draw(frontend::Painter & pain, bool paint_caret)
|
||||
{
|
||||
if (height_ == 0 || width_ == 0)
|
||||
return;
|
||||
@ -3059,19 +3070,35 @@ void BufferView::draw(frontend::Painter & pain)
|
||||
int const y = tm.first().second->position();
|
||||
PainterInfo pi(this, pain);
|
||||
|
||||
CursorSlice const & bottomSlice = d->cursor_.bottom();
|
||||
/** A repaint of the previous cursor row is needed if
|
||||
* 1/ the caret will be painted and is is not the same than the
|
||||
* already painted one;
|
||||
* 2/ the caret will not be painted, but there is already one on
|
||||
* screen.
|
||||
*/
|
||||
d->repaint_caret_row_ = (paint_caret && bottomSlice != d->caret_slice_)
|
||||
|| (! paint_caret && !d->caret_slice_.empty());
|
||||
|
||||
// Check whether the row where the cursor lives needs to be scrolled.
|
||||
// Update the drawing strategy if needed.
|
||||
checkCursorScrollOffset(pi);
|
||||
checkCursorScrollOffset();
|
||||
|
||||
switch (d->update_strategy_) {
|
||||
|
||||
case NoScreenUpdate:
|
||||
// If no screen painting is actually needed, only some the different
|
||||
// coordinates of insets and paragraphs needs to be updated.
|
||||
// no screen painting is actually needed. In nodraw stage
|
||||
// however, the different coordinates of insets and paragraphs
|
||||
// needs to be updated.
|
||||
LYXERR(Debug::PAINTING, "Strategy: NoScreenUpdate");
|
||||
pi.full_repaint = true;
|
||||
pi.pain.setDrawingEnabled(false);
|
||||
tm.draw(pi, 0, y);
|
||||
pi.full_repaint = false;
|
||||
if (pain.isNull()) {
|
||||
pi.full_repaint = true;
|
||||
tm.draw(pi, 0, y);
|
||||
} else if (d->repaint_caret_row_) {
|
||||
pi.full_repaint = false;
|
||||
tm.draw(pi, 0, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case SingleParUpdate:
|
||||
@ -3134,6 +3161,12 @@ void BufferView::draw(frontend::Painter & pain)
|
||||
}
|
||||
LYXERR(Debug::PAINTING, "Found new anchor pit = " << d->anchor_pit_
|
||||
<< " anchor ypos = " << d->anchor_ypos_);
|
||||
|
||||
// Remember what has just been done for the next draw() step
|
||||
if (paint_caret)
|
||||
d->caret_slice_ = bottomSlice;
|
||||
else
|
||||
d->caret_slice_ = CursorSlice();
|
||||
}
|
||||
|
||||
|
||||
|
@ -42,10 +42,10 @@ class FuncStatus;
|
||||
class Intl;
|
||||
class Inset;
|
||||
class Length;
|
||||
class PainterInfo;
|
||||
class ParIterator;
|
||||
class ParagraphMetrics;
|
||||
class Point;
|
||||
class Row;
|
||||
class TexRow;
|
||||
class Text;
|
||||
class TextMetrics;
|
||||
@ -132,6 +132,9 @@ public:
|
||||
/// Only to be called with good y coordinates (after a bv::metrics)
|
||||
bool needsFitCursor() const;
|
||||
|
||||
/// returns true if this row needs to be repainted (to erase caret)
|
||||
bool needRepaint(Text const * text, Row const & row) const;
|
||||
|
||||
// Returns the amount of horizontal scrolling applied to the
|
||||
// top-level row where the cursor lies
|
||||
int horizScrollOffset() const;
|
||||
@ -283,6 +286,10 @@ public:
|
||||
/// update the internal \c ViewMetricsInfo.
|
||||
void updateMetrics();
|
||||
|
||||
// this is the "nodraw" drawing stage: only set the positions of the
|
||||
// insets in metrics cache.
|
||||
void updatePosCache();
|
||||
|
||||
///
|
||||
TextMetrics const & textMetrics(Text const * t) const;
|
||||
TextMetrics & textMetrics(Text const * t);
|
||||
@ -300,12 +307,11 @@ public:
|
||||
bool paragraphVisible(DocIterator const & dit) const;
|
||||
/// is the cursor currently visible in the view
|
||||
bool cursorInView(Point const & p, int h) const;
|
||||
/// get the position and height of the cursor
|
||||
void cursorPosAndHeight(Point & p, int & h) const;
|
||||
|
||||
/// get the position and height of the caret
|
||||
void caretPosAndHeight(Point & p, int & h) const;
|
||||
|
||||
///
|
||||
void draw(frontend::Painter & pain);
|
||||
void draw(frontend::Painter & pain, bool paint_caret);
|
||||
|
||||
/// get this view's keyboard map handler.
|
||||
Intl & getIntl();
|
||||
@ -367,7 +373,7 @@ private:
|
||||
|
||||
// Check whether the row where the cursor lives needs to be scrolled.
|
||||
// Update the drawing strategy if needed.
|
||||
void checkCursorScrollOffset(PainterInfo & pi);
|
||||
void checkCursorScrollOffset();
|
||||
|
||||
/// The minimal size of the document that is visible. Used
|
||||
/// when it is allowed to scroll below the document.
|
||||
|
@ -454,19 +454,6 @@ int Cursor::currentMode()
|
||||
}
|
||||
|
||||
|
||||
bool Cursor::inCoordCache() const
|
||||
{
|
||||
// the root inset is not in cache, but we do not need it.
|
||||
if (depth() == 1)
|
||||
return true;
|
||||
CoordCache::Insets const & icache = bv_->coordCache().getInsets();
|
||||
for (size_t i = 1 ; i < depth() ; ++i)
|
||||
if (!icache.has(&(*this)[i].inset()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Cursor::getPos(int & x, int & y) const
|
||||
{
|
||||
Point p = bv().getPos(*this);
|
||||
|
@ -216,8 +216,6 @@ public:
|
||||
/// are we entering a macro name?
|
||||
bool & macromode() { return macromode_; }
|
||||
|
||||
/// returns true when all insets in cursor stack are in cache
|
||||
bool inCoordCache() const;
|
||||
/// returns x,y position
|
||||
void getPos(int & x, int & y) const;
|
||||
/// return logical positions between which the cursor is situated
|
||||
|
@ -59,7 +59,7 @@ namespace {
|
||||
|
||||
// The format should also be updated in configure.py, and conversion code
|
||||
// should be added to prefs2prefs_prefs.py.
|
||||
static unsigned int const LYXRC_FILEFORMAT = 22; // ef: pygmentize_command
|
||||
static unsigned int const LYXRC_FILEFORMAT = 23; // lasgouttes: remove qimage
|
||||
|
||||
// when adding something to this array keep it sorted!
|
||||
LexerKeyword lyxrcTags[] = {
|
||||
@ -195,7 +195,6 @@ LexerKeyword lyxrcTags[] = {
|
||||
{ "\\use_converter_needauth_forbidden", LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN },
|
||||
{ "\\use_lastfilepos", LyXRC::RC_USELASTFILEPOS },
|
||||
{ "\\use_pixmap_cache", LyXRC::RC_USE_PIXMAP_CACHE },
|
||||
{ "\\use_qimage", LyXRC::RC_USE_QIMAGE },
|
||||
// compatibility with versions older than 1.4.0 only
|
||||
{ "\\use_system_colors", LyXRC::RC_USE_SYSTEM_COLORS },
|
||||
{ "\\use_system_theme_icons", LyXRC::RC_USE_SYSTEM_THEME_ICONS },
|
||||
@ -327,7 +326,6 @@ void LyXRC::setDefaults()
|
||||
use_system_colors = false;
|
||||
use_tooltip = true;
|
||||
use_pixmap_cache = false;
|
||||
use_qimage = true;
|
||||
converter_cache_maxage = 6 * 30 * 24 * 3600; // 6 months
|
||||
user_name = to_utf8(support::user_name());
|
||||
user_email = to_utf8(support::user_email());
|
||||
@ -880,9 +878,6 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
|
||||
case RC_USE_PIXMAP_CACHE:
|
||||
lexrc >> use_pixmap_cache;
|
||||
break;
|
||||
case RC_USE_QIMAGE:
|
||||
lexrc >> use_qimage;
|
||||
break;
|
||||
case RC_SPELLCHECKER:
|
||||
lexrc >> spellchecker;
|
||||
break;
|
||||
@ -2434,15 +2429,6 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
// fall through
|
||||
case RC_USE_QIMAGE:
|
||||
if (ignore_system_lyxrc ||
|
||||
use_qimage != system_lyxrc.use_qimage) {
|
||||
os << "\\use_qimage "
|
||||
<< convert<string>(use_qimage)
|
||||
<< '\n';
|
||||
}
|
||||
if (tag != RC_LAST)
|
||||
break;
|
||||
|
||||
os << "\n#\n"
|
||||
<< "# LANGUAGE SUPPORT SECTION ##########################\n"
|
||||
@ -3016,7 +3002,6 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
|
||||
case LyXRC::RC_USE_SYSTEM_COLORS:
|
||||
case LyXRC::RC_USE_TOOLTIP:
|
||||
case LyXRC::RC_USE_PIXMAP_CACHE:
|
||||
case LyXRC::RC_USE_QIMAGE:
|
||||
case LyXRC::RC_USE_SYSTEM_THEME_ICONS:
|
||||
case LyXRC::RC_VIEWDVI_PAPEROPTION:
|
||||
case LyXRC::RC_SINGLE_CLOSE_TAB_BUTTON:
|
||||
|
@ -174,7 +174,6 @@ public:
|
||||
RC_USE_SYSTEM_COLORS,
|
||||
RC_USE_TOOLTIP,
|
||||
RC_USE_PIXMAP_CACHE,
|
||||
RC_USE_QIMAGE,
|
||||
RC_USE_SYSTEM_THEME_ICONS,
|
||||
RC_VIEWDVI_PAPEROPTION,
|
||||
RC_VIEWER,
|
||||
@ -334,8 +333,6 @@ public:
|
||||
bool use_system_colors;
|
||||
/// Use pixmap cache?
|
||||
bool use_pixmap_cache;
|
||||
/// Use QImage backend?
|
||||
bool use_qimage;
|
||||
/// Spellchecker engine: aspell, hunspell, etc
|
||||
std::string spellchecker;
|
||||
/// Alternate language for spellchecker
|
||||
|
@ -130,7 +130,7 @@ public:
|
||||
bool selected;
|
||||
/// Whether the spell checker is enabled for the parent
|
||||
bool do_spellcheck;
|
||||
///
|
||||
/// True when it can be assumed that the screen has been cleared
|
||||
bool full_repaint;
|
||||
/// Current background color
|
||||
ColorCode background_color;
|
||||
|
@ -576,7 +576,7 @@ void RowPainter::paintText()
|
||||
paintStringAndSel(e);
|
||||
|
||||
// Paint the spelling marks if enabled.
|
||||
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && pi_.pain.isDrawingEnabled())
|
||||
if (lyxrc.spellcheck_continuously && pi_.do_spellcheck && !pi_.pain.isNull())
|
||||
paintMisspelledMark(e);
|
||||
break;
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
|
||||
#include "frontends/FontMetrics.h"
|
||||
#include "frontends/Painter.h"
|
||||
#include "frontends/NullPainter.h"
|
||||
|
||||
#include "support/debug.h"
|
||||
#include "support/lassert.h"
|
||||
@ -198,6 +199,14 @@ bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width)
|
||||
}
|
||||
|
||||
|
||||
void TextMetrics::updatePosCache(pit_type pit) const
|
||||
{
|
||||
frontend::NullPainter np;
|
||||
PainterInfo pi(bv_, np);
|
||||
drawParagraph(pi, pit, origin_.x_, par_metrics_[pit].position());
|
||||
}
|
||||
|
||||
|
||||
int TextMetrics::rightMargin(ParagraphMetrics const & pm) const
|
||||
{
|
||||
return text_->isMainText() ? pm.rightMargin(*bv_) : 0;
|
||||
@ -1220,6 +1229,7 @@ void TextMetrics::newParMetricsDown()
|
||||
redoParagraph(pit);
|
||||
par_metrics_[pit].setPosition(last.second.position()
|
||||
+ last.second.descent() + par_metrics_[pit].ascent());
|
||||
updatePosCache(pit);
|
||||
}
|
||||
|
||||
|
||||
@ -1234,6 +1244,7 @@ void TextMetrics::newParMetricsUp()
|
||||
redoParagraph(pit);
|
||||
par_metrics_[pit].setPosition(first.second.position()
|
||||
- first.second.ascent() - par_metrics_[pit].descent());
|
||||
updatePosCache(pit);
|
||||
}
|
||||
|
||||
// y is screen coordinate
|
||||
@ -1803,8 +1814,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
|
||||
return;
|
||||
size_t const nrows = pm.rows().size();
|
||||
|
||||
// Use fast lane when drawing is disabled.
|
||||
if (!pi.pain.isDrawingEnabled()) {
|
||||
// Use fast lane in nodraw stage.
|
||||
if (pi.pain.isNull()) {
|
||||
for (size_t i = 0; i != nrows; ++i) {
|
||||
|
||||
Row const & row = pm.rows()[i];
|
||||
@ -1856,17 +1867,11 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
|
||||
if (i)
|
||||
y += row.ascent();
|
||||
|
||||
RowPainter rp(pi, *text_, row, row_x, y);
|
||||
|
||||
// It is not needed to draw on screen if we are not inside.
|
||||
bool const inside = (y + row.descent() >= 0
|
||||
&& y - row.ascent() < ww);
|
||||
pi.pain.setDrawingEnabled(inside);
|
||||
if (!inside) {
|
||||
// Paint only the insets to set inset cache correctly
|
||||
// FIXME: remove paintOnlyInsets when we know that positions
|
||||
// have already been set.
|
||||
rp.paintOnlyInsets();
|
||||
// Inset positions have already been set in nodraw stage.
|
||||
y += row.descent();
|
||||
continue;
|
||||
}
|
||||
@ -1888,13 +1893,16 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
|
||||
// Row signature; has row changed since last paint?
|
||||
row.setCrc(pm.computeRowSignature(row, *bv_));
|
||||
bool row_has_changed = row.changed()
|
||||
|| bv_->hadHorizScrollOffset(text_, pit, row.pos());
|
||||
|| bv_->hadHorizScrollOffset(text_, pit, row.pos())
|
||||
|| bv_->needRepaint(text_, row);
|
||||
|
||||
// Take this opportunity to spellcheck the row contents.
|
||||
if (row_has_changed && pi.do_spellcheck && lyxrc.spellcheck_continuously) {
|
||||
text_->getPar(pit).spellCheck();
|
||||
}
|
||||
|
||||
RowPainter rp(pi, *text_, row, row_x, y);
|
||||
|
||||
// Don't paint the row if a full repaint has not been requested
|
||||
// and if it has not changed.
|
||||
if (!pi.full_repaint && !row_has_changed) {
|
||||
@ -1925,7 +1933,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
|
||||
<< " row_selection=" << row.selection()
|
||||
<< " full_repaint=" << pi.full_repaint
|
||||
<< " row_has_changed=" << row_has_changed
|
||||
<< " drawingEnabled=" << pi.pain.isDrawingEnabled());
|
||||
<< " null painter=" << pi.pain.isNull());
|
||||
}
|
||||
|
||||
// Backup full_repaint status and force full repaint
|
||||
@ -1953,8 +1961,6 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
|
||||
// Restore full_repaint status.
|
||||
pi.full_repaint = tmp;
|
||||
}
|
||||
// Re-enable screen drawing for future use of the painter.
|
||||
pi.pain.setDrawingEnabled(true);
|
||||
|
||||
//LYXERR(Debug::PAINTING, ".");
|
||||
}
|
||||
|
@ -62,6 +62,11 @@ public:
|
||||
///
|
||||
void newParMetricsUp();
|
||||
|
||||
/// The "nodraw" drawing stage for one single paragraph: set the
|
||||
/// positions of the insets contained this paragraph in metrics
|
||||
/// cache. Related to BufferView::updatePosCache.
|
||||
void updatePosCache(pit_type pit) const;
|
||||
|
||||
/// Gets the fully instantiated font at a given position in a paragraph
|
||||
/// Basically the same routine as Paragraph::getFont() in Paragraph.cpp.
|
||||
/// The difference is that this one is used for displaying, and thus we
|
||||
|
@ -17,6 +17,7 @@ liblyxfrontends_a_SOURCES = \
|
||||
Delegates.h \
|
||||
KeyModifier.h \
|
||||
KeySymbol.h \
|
||||
NullPainter.h \
|
||||
Painter.h \
|
||||
Clipboard.h \
|
||||
Selection.h \
|
||||
|
109
src/frontends/NullPainter.h
Normal file
109
src/frontends/NullPainter.h
Normal file
@ -0,0 +1,109 @@
|
||||
// -*- C++ -*-
|
||||
/**
|
||||
* \file NullPainter.h
|
||||
* This file is part of LyX, the document processor.
|
||||
* Licence details can be found in the file COPYING.
|
||||
*
|
||||
* \author unknown
|
||||
* \author John Levon
|
||||
* \author Jean-Marc Lasgouttes
|
||||
*
|
||||
* Full author contact details are available in file CREDITS.
|
||||
*/
|
||||
|
||||
#ifndef NULLPAINTER_H
|
||||
#define NULLPAINTER_H
|
||||
|
||||
#include "Painter.h"
|
||||
|
||||
namespace lyx {
|
||||
|
||||
namespace frontend {
|
||||
|
||||
/**
|
||||
* NullPainter - A painter instance that does nothing
|
||||
*/
|
||||
class NullPainter : public Painter {
|
||||
public:
|
||||
NullPainter() : Painter(1) {}
|
||||
|
||||
~NullPainter() {}
|
||||
|
||||
/// draw a line from point to point
|
||||
void line(int, int, int, int, Color,
|
||||
line_style = line_solid, int = thin_line) {}
|
||||
|
||||
///
|
||||
void lines(int const *, int const *, int, Color,
|
||||
fill_style = fill_none, line_style = line_solid,
|
||||
int = thin_line) {}
|
||||
|
||||
///
|
||||
void path(int const *, int const *, int const *, int const *,
|
||||
int const *, int const *, int, Color,
|
||||
fill_style = fill_none, line_style = line_solid, int = thin_line) {}
|
||||
|
||||
/// draw a rectangle
|
||||
void rectangle(int, int, int, int, Color,
|
||||
line_style = line_solid, int = thin_line) {}
|
||||
|
||||
/// draw a filled rectangle
|
||||
void fillRectangle(int, int, int, int, Color) {}
|
||||
|
||||
/// draw an arc
|
||||
void arc(int, int, unsigned int, unsigned int, int, int, Color) {}
|
||||
|
||||
/// draw a pixel
|
||||
void point(int, int, Color) {}
|
||||
|
||||
/// draw an image from the image cache
|
||||
void image(int, int, int, int, graphics::Image const &) {}
|
||||
|
||||
/// draw a string
|
||||
void text(int, int, docstring const &, FontInfo const &) {}
|
||||
|
||||
/// draw a char
|
||||
void text(int, int, char_type, FontInfo const &) {}
|
||||
|
||||
/// draw a string
|
||||
void text(int, int, docstring const &, Font const &, double, double) {}
|
||||
|
||||
///
|
||||
void text(int, int, docstring const &, Font const &,
|
||||
Color, size_type, size_type, double, double) {}
|
||||
|
||||
/// This painter does not paint
|
||||
bool isNull() const { return true; }
|
||||
|
||||
/// draw the underbar, strikeout, xout, uuline and uwave font attributes
|
||||
void textDecoration(FontInfo const &, int, int, int) {}
|
||||
|
||||
/**
|
||||
* Draw a string and enclose it inside a rectangle. If
|
||||
* back color is specified, the background is cleared with
|
||||
* the given color. If frame is specified, a thin frame is drawn
|
||||
* around the text with the given color.
|
||||
*/
|
||||
void rectText(int, int, docstring const &,
|
||||
FontInfo const &, Color, Color) {}
|
||||
|
||||
/// draw a string and enclose it inside a button frame
|
||||
void buttonText(int, int, docstring const &,
|
||||
FontInfo const &, Color, Color, int) {}
|
||||
|
||||
/// draw a character of a preedit string for cjk support.
|
||||
int preeditText(int, int, char_type, FontInfo const &,
|
||||
preedit_style) { return 0; }
|
||||
|
||||
/// start monochrome painting mode, i.e. map every color into [min,max]
|
||||
void enterMonochromeMode(Color const &, Color const &) {}
|
||||
/// leave monochrome painting mode
|
||||
void leaveMonochromeMode() {}
|
||||
/// draws a wavy line that can be used for underlining.
|
||||
void wavyHorizontalLine(int, int, int, ColorCode) {}
|
||||
};
|
||||
|
||||
} // namespace frontend
|
||||
} // namespace lyx
|
||||
|
||||
#endif // NULLPAINTER_H
|
@ -147,11 +147,8 @@ public:
|
||||
Color other, size_type from, size_type to,
|
||||
double wordspacing, double textwidth) = 0;
|
||||
|
||||
void setDrawingEnabled(bool drawing_enabled)
|
||||
{ drawing_enabled_ = drawing_enabled; }
|
||||
|
||||
/// Indicate wether real screen drawing shall be done or not.
|
||||
bool isDrawingEnabled() const { return drawing_enabled_; }
|
||||
// Returns true if the painter does not actually paint.
|
||||
virtual bool isNull() const = 0;
|
||||
|
||||
double pixelRatio() const { return pixel_ratio_; }
|
||||
|
||||
|
@ -65,8 +65,8 @@ FindAndReplaceWidget::FindAndReplaceWidget(GuiView & view)
|
||||
replace_work_area_->setFrameStyle(QFrame::StyledPanel);
|
||||
|
||||
// We don't want two cursors blinking.
|
||||
find_work_area_->stopBlinkingCursor();
|
||||
replace_work_area_->stopBlinkingCursor();
|
||||
find_work_area_->stopBlinkingCaret();
|
||||
replace_work_area_->stopBlinkingCaret();
|
||||
}
|
||||
|
||||
|
||||
|
@ -122,7 +122,6 @@
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <QX11Info>
|
||||
#undef CursorShape
|
||||
#undef None
|
||||
#elif defined(QPA_XCB)
|
||||
#include <xcb/xcb.h>
|
||||
@ -1439,7 +1438,7 @@ void GuiApplication::updateCurrentView(FuncRequest const & cmd, DispatchResult &
|
||||
theSelection().haveSelection(bv->cursor().selection());
|
||||
|
||||
// update gui
|
||||
current_view_->restartCursor();
|
||||
current_view_->restartCaret();
|
||||
}
|
||||
if (dr.needMessageUpdate()) {
|
||||
// Some messages may already be translated, so we cannot use _()
|
||||
@ -2158,7 +2157,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
|
||||
if (!keysym.isOK())
|
||||
LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
|
||||
if (current_view_)
|
||||
current_view_->restartCursor();
|
||||
current_view_->restartCaret();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2218,7 +2217,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
|
||||
if (!isPrintable(encoded_last_key)) {
|
||||
LYXERR(Debug::KEY, "Non-printable character! Omitting.");
|
||||
if (current_view_)
|
||||
current_view_->restartCursor();
|
||||
current_view_->restartCaret();
|
||||
return;
|
||||
}
|
||||
// The following modifier check is not needed on Mac.
|
||||
@ -2240,7 +2239,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
|
||||
{
|
||||
if (current_view_) {
|
||||
current_view_->message(_("Unknown function."));
|
||||
current_view_->restartCursor();
|
||||
current_view_->restartCaret();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -2255,7 +2254,7 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
|
||||
LYXERR(Debug::KEY, "Unknown Action and not isText() -- giving up");
|
||||
if (current_view_) {
|
||||
current_view_->message(_("Unknown function."));
|
||||
current_view_->restartCursor();
|
||||
current_view_->restartCaret();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -171,9 +171,6 @@ void GuiPainter::leaveMonochromeMode()
|
||||
|
||||
void GuiPainter::point(int x, int y, Color col)
|
||||
{
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
setQPainterPen(computeColor(col));
|
||||
drawPoint(x, y);
|
||||
}
|
||||
@ -184,9 +181,6 @@ void GuiPainter::line(int x1, int y1, int x2, int y2,
|
||||
line_style ls,
|
||||
int lw)
|
||||
{
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
setQPainterPen(computeColor(col), ls, lw);
|
||||
bool const do_antialiasing = renderHints() & TextAntialiasing
|
||||
&& x1 != x2 && y1 != y2 && ls != line_solid_aliased;
|
||||
@ -202,9 +196,6 @@ void GuiPainter::lines(int const * xp, int const * yp, int np,
|
||||
line_style ls,
|
||||
int lw)
|
||||
{
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
// double the size if needed
|
||||
// FIXME THREAD
|
||||
static QVector<QPoint> points(32);
|
||||
@ -247,9 +238,6 @@ void GuiPainter::path(int const * xp, int const * yp,
|
||||
line_style ls,
|
||||
int lw)
|
||||
{
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
QPainterPath bpath;
|
||||
// This is the starting point, so its control points are meaningless
|
||||
bpath.moveTo(xp[0], yp[0]);
|
||||
@ -278,9 +266,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
|
||||
line_style ls,
|
||||
int lw)
|
||||
{
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
setQPainterPen(computeColor(col), ls, lw);
|
||||
drawRect(x, y, w, h);
|
||||
}
|
||||
@ -288,9 +273,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
|
||||
|
||||
void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
|
||||
{
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
fillRect(x, y, w, h, guiApp->colorCache().get(col));
|
||||
}
|
||||
|
||||
@ -298,9 +280,6 @@ void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
|
||||
void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
|
||||
int a1, int a2, Color col)
|
||||
{
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
// LyX usings 1/64ths degree, Qt usings 1/16th
|
||||
setQPainterPen(computeColor(col));
|
||||
bool const do_antialiasing = renderHints() & TextAntialiasing;
|
||||
@ -317,9 +296,6 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
|
||||
|
||||
fillRectangle(x, y, w, h, Color_graphicsbg);
|
||||
|
||||
if (!isDrawingEnabled())
|
||||
return;
|
||||
|
||||
QImage const image = qlimage.image();
|
||||
QRectF const drect = QRectF(x, y, w, h);
|
||||
QRectF const srect = QRectF(0, 0, image.width(), image.height());
|
||||
@ -391,7 +367,7 @@ void GuiPainter::text(int x, int y, docstring const & s,
|
||||
double const wordspacing, double const tw)
|
||||
{
|
||||
//LYXERR0("text: x=" << x << ", s=" << s);
|
||||
if (s.empty() || !isDrawingEnabled())
|
||||
if (s.empty())
|
||||
return;
|
||||
|
||||
/* Caution: The following ucs4 to QString conversions work for symbol fonts
|
||||
|
@ -37,6 +37,9 @@ public:
|
||||
GuiPainter(QPaintDevice *, double pixel_ratio);
|
||||
virtual ~GuiPainter();
|
||||
|
||||
/// This painter paints
|
||||
virtual bool isNull() const { return false; }
|
||||
|
||||
/// draw a line from point to point
|
||||
virtual void line(
|
||||
int x1, int y1,
|
||||
|
@ -4351,13 +4351,13 @@ Buffer const * GuiView::updateInset(Inset const * inset)
|
||||
}
|
||||
|
||||
|
||||
void GuiView::restartCursor()
|
||||
void GuiView::restartCaret()
|
||||
{
|
||||
/* When we move around, or type, it's nice to be able to see
|
||||
* the cursor immediately after the keypress.
|
||||
* the caret immediately after the keypress.
|
||||
*/
|
||||
if (d.current_work_area_)
|
||||
d.current_work_area_->startBlinkingCursor();
|
||||
d.current_work_area_->startBlinkingCaret();
|
||||
|
||||
// Take this occasion to update the other GUI elements.
|
||||
updateDialogs();
|
||||
@ -4426,7 +4426,7 @@ void GuiView::resetDialogs()
|
||||
// Now update controls with current buffer.
|
||||
guiApp->setCurrentView(this);
|
||||
restoreLayout();
|
||||
restartCursor();
|
||||
restartCaret();
|
||||
}
|
||||
|
||||
|
||||
|
@ -116,7 +116,7 @@ public:
|
||||
/// \return true if the \c FuncRequest has been dispatched.
|
||||
void dispatch(FuncRequest const & cmd, DispatchResult & dr);
|
||||
|
||||
void restartCursor();
|
||||
void restartCaret();
|
||||
/// Update the completion popup and the inline completion state.
|
||||
/// If \c start is true, then a new completion might be started.
|
||||
/// If \c keep is true, an active completion will be kept active
|
||||
|
@ -16,7 +16,13 @@
|
||||
|
||||
#include "ColorCache.h"
|
||||
#include "FontLoader.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "GuiCompleter.h"
|
||||
#include "GuiKeySymbol.h"
|
||||
#include "GuiPainter.h"
|
||||
#include "GuiView.h"
|
||||
#include "Menus.h"
|
||||
#include "qt_helpers.h"
|
||||
|
||||
#include "Buffer.h"
|
||||
#include "BufferList.h"
|
||||
@ -26,19 +32,14 @@
|
||||
#include "Cursor.h"
|
||||
#include "Font.h"
|
||||
#include "FuncRequest.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "GuiCompleter.h"
|
||||
#include "GuiKeySymbol.h"
|
||||
#include "GuiPainter.h"
|
||||
#include "GuiView.h"
|
||||
#include "KeySymbol.h"
|
||||
#include "Language.h"
|
||||
#include "LyX.h"
|
||||
#include "LyXRC.h"
|
||||
#include "LyXVC.h"
|
||||
#include "qt_helpers.h"
|
||||
#include "Text.h"
|
||||
#include "TextMetrics.h"
|
||||
#include "Undo.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "graphics/GraphicsImage.h"
|
||||
@ -46,7 +47,6 @@
|
||||
|
||||
#include "support/convert.h"
|
||||
#include "support/debug.h"
|
||||
#include "support/gettext.h"
|
||||
#include "support/lassert.h"
|
||||
#include "support/TempFile.h"
|
||||
|
||||
@ -68,7 +68,6 @@
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QPixmapCache>
|
||||
#include <QScrollBar>
|
||||
#include <QStyleOption>
|
||||
#include <QStylePainter>
|
||||
@ -77,8 +76,6 @@
|
||||
#include <QToolTip>
|
||||
#include <QMenuBar>
|
||||
|
||||
#include "support/bind.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
@ -130,17 +127,15 @@ mouse_button::state q_motion_state(Qt::MouseButtons state)
|
||||
|
||||
namespace frontend {
|
||||
|
||||
class CursorWidget {
|
||||
class CaretWidget {
|
||||
public:
|
||||
CursorWidget() : rtl_(false), l_shape_(false), completable_(false),
|
||||
show_(false), x_(0), cursor_width_(0)
|
||||
{
|
||||
recomputeWidth();
|
||||
}
|
||||
CaretWidget() : rtl_(false), l_shape_(false), completable_(false),
|
||||
x_(0), caret_width_(0)
|
||||
{}
|
||||
|
||||
void draw(QPainter & painter)
|
||||
{
|
||||
if (!show_ || !rect_.isValid())
|
||||
if (!rect_.isValid())
|
||||
return;
|
||||
|
||||
int y = rect_.top();
|
||||
@ -149,7 +144,7 @@ public:
|
||||
int bot = rect_.bottom();
|
||||
|
||||
// draw vertical line
|
||||
painter.fillRect(x_, y, cursor_width_, rect_.height(), color_);
|
||||
painter.fillRect(x_, y, caret_width_, rect_.height(), color_);
|
||||
|
||||
// draw RTL/LTR indication
|
||||
painter.setPen(color_);
|
||||
@ -157,7 +152,7 @@ public:
|
||||
if (rtl_)
|
||||
painter.drawLine(x_, bot, x_ - l, bot);
|
||||
else
|
||||
painter.drawLine(x_, bot, x_ + cursor_width_ + r, bot);
|
||||
painter.drawLine(x_, bot, x_ + caret_width_ + r, bot);
|
||||
}
|
||||
|
||||
// draw completion triangle
|
||||
@ -168,8 +163,8 @@ public:
|
||||
painter.drawLine(x_ - 1, m - d, x_ - 1 - d, m);
|
||||
painter.drawLine(x_ - 1, m + d, x_ - 1 - d, m);
|
||||
} else {
|
||||
painter.drawLine(x_ + cursor_width_, m - d, x_ + cursor_width_ + d, m);
|
||||
painter.drawLine(x_ + cursor_width_, m + d, x_ + cursor_width_ + d, m);
|
||||
painter.drawLine(x_ + caret_width_, m - d, x_ + caret_width_ + d, m);
|
||||
painter.drawLine(x_ + caret_width_, m + d, x_ + caret_width_ + d, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -203,38 +198,32 @@ public:
|
||||
r = max(r, TabIndicatorWidth);
|
||||
}
|
||||
|
||||
// compute overall rectangle
|
||||
rect_ = QRect(x - l, y, cursor_width_ + r + l, h);
|
||||
}
|
||||
|
||||
void show(bool set_show = true) { show_ = set_show; }
|
||||
void hide() { show_ = false; }
|
||||
int cursorWidth() const { return cursor_width_; }
|
||||
void recomputeWidth() {
|
||||
cursor_width_ = lyxrc.cursor_width
|
||||
//FIXME: LyXRC::cursor_width should be caret_width
|
||||
caret_width_ = lyxrc.cursor_width
|
||||
? lyxrc.cursor_width
|
||||
: 1 + int((lyxrc.currentZoom + 50) / 200.0);
|
||||
|
||||
// compute overall rectangle
|
||||
rect_ = QRect(x - l, y, caret_width_ + r + l, h);
|
||||
}
|
||||
|
||||
QRect const & rect() { return rect_; }
|
||||
|
||||
private:
|
||||
/// cursor is in RTL or LTR text
|
||||
/// caret is in RTL or LTR text
|
||||
bool rtl_;
|
||||
/// indication for RTL or LTR
|
||||
bool l_shape_;
|
||||
/// triangle to show that a completion is available
|
||||
bool completable_;
|
||||
///
|
||||
bool show_;
|
||||
///
|
||||
QColor color_;
|
||||
/// rectangle, possibly with l_shape and completion triangle
|
||||
QRect rect_;
|
||||
/// x position (were the vertical line is drawn)
|
||||
int x_;
|
||||
|
||||
int cursor_width_;
|
||||
/// the width of the vertical blinking bar
|
||||
int caret_width_;
|
||||
};
|
||||
|
||||
|
||||
@ -246,13 +235,35 @@ SyntheticMouseEvent::SyntheticMouseEvent()
|
||||
|
||||
|
||||
GuiWorkArea::Private::Private(GuiWorkArea * parent)
|
||||
: p(parent), screen_(0), buffer_view_(0), lyx_view_(0),
|
||||
cursor_visible_(false), cursor_(0),
|
||||
: p(parent), buffer_view_(0), lyx_view_(0),
|
||||
caret_(0), caret_visible_(false),
|
||||
need_resize_(false), schedule_redraw_(false), preedit_lines_(1),
|
||||
pixel_ratio_(1.0),
|
||||
completer_(new GuiCompleter(p, p)), dialog_mode_(false), shell_escape_(false),
|
||||
read_only_(false), clean_(true), externally_modified_(false)
|
||||
{
|
||||
int const time = QApplication::cursorFlashTime() / 2;
|
||||
if (time > 0) {
|
||||
caret_timeout_.setInterval(time);
|
||||
caret_timeout_.start();
|
||||
} else {
|
||||
// let's initialize this just to be safe
|
||||
caret_timeout_.setInterval(500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GuiWorkArea::Private::~Private()
|
||||
{
|
||||
// If something is wrong with the buffer, we can ignore it safely
|
||||
try {
|
||||
buffer_view_->buffer().workAreaManager().remove(p);
|
||||
} catch(...) {}
|
||||
delete buffer_view_;
|
||||
delete caret_;
|
||||
// Completer has a QObject parent and is thus automatically destroyed.
|
||||
// See #4758.
|
||||
// delete completer_;
|
||||
}
|
||||
|
||||
|
||||
@ -285,24 +296,18 @@ double GuiWorkArea::pixelRatio() const
|
||||
void GuiWorkArea::init()
|
||||
{
|
||||
// Setup the signals
|
||||
connect(&d->cursor_timeout_, SIGNAL(timeout()),
|
||||
this, SLOT(toggleCursor()));
|
||||
connect(&d->caret_timeout_, SIGNAL(timeout()),
|
||||
this, SLOT(toggleCaret()));
|
||||
|
||||
int const time = QApplication::cursorFlashTime() / 2;
|
||||
if (time > 0) {
|
||||
d->cursor_timeout_.setInterval(time);
|
||||
d->cursor_timeout_.start();
|
||||
} else {
|
||||
// let's initialize this just to be safe
|
||||
d->cursor_timeout_.setInterval(500);
|
||||
}
|
||||
// This connection is closed at the same time as this is destroyed.
|
||||
d->synthetic_mouse_event_.timeout.timeout.connect([this](){
|
||||
generateSyntheticMouseEvent();
|
||||
});
|
||||
|
||||
d->resetScreen();
|
||||
// With Qt4.5 a mouse event will happen before the first paint event
|
||||
// so make sure that the buffer view has an up to date metrics.
|
||||
d->buffer_view_->resize(viewport()->width(), viewport()->height());
|
||||
d->cursor_ = new frontend::CursorWidget();
|
||||
d->cursor_->hide();
|
||||
d->caret_ = new frontend::CaretWidget();
|
||||
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
setAcceptDrops(true);
|
||||
@ -311,63 +316,33 @@ void GuiWorkArea::init()
|
||||
setFrameStyle(QFrame::NoFrame);
|
||||
updateWindowTitle();
|
||||
|
||||
viewport()->setAutoFillBackground(false);
|
||||
// We don't need double-buffering nor SystemBackground on
|
||||
// the viewport because we have our own backing pixmap.
|
||||
viewport()->setAttribute(Qt::WA_NoSystemBackground);
|
||||
d->updateCursorShape();
|
||||
|
||||
// we paint our own background
|
||||
viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
|
||||
d->setCursorShape(Qt::IBeamCursor);
|
||||
|
||||
// This connection is closed at the same time as this is destroyed.
|
||||
d->synthetic_mouse_event_.timeout.timeout.connect([this](){
|
||||
generateSyntheticMouseEvent();
|
||||
});
|
||||
|
||||
LYXERR(Debug::GUI, "viewport width: " << viewport()->width()
|
||||
<< " viewport height: " << viewport()->height());
|
||||
|
||||
// Enables input methods for asian languages.
|
||||
// Must be set when creating custom text editing widgets.
|
||||
setAttribute(Qt::WA_InputMethodEnabled, true);
|
||||
|
||||
d->dialog_mode_ = false;
|
||||
}
|
||||
|
||||
|
||||
GuiWorkArea::~GuiWorkArea()
|
||||
{
|
||||
// If something is wrong with the buffer, we can ignore it safely
|
||||
try {
|
||||
d->buffer_view_->buffer().workAreaManager().remove(this);
|
||||
} catch(...) {}
|
||||
delete d->screen_;
|
||||
delete d->buffer_view_;
|
||||
delete d->cursor_;
|
||||
// Completer has a QObject parent and is thus automatically destroyed.
|
||||
// See #4758.
|
||||
// delete completer_;
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
Qt::CursorShape GuiWorkArea::cursorShape() const
|
||||
{
|
||||
return viewport()->cursor().shape();
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::setCursorShape(Qt::CursorShape shape)
|
||||
{
|
||||
p->viewport()->setCursor(shape);
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::updateCursorShape()
|
||||
{
|
||||
setCursorShape(buffer_view_->clickableInset()
|
||||
? Qt::PointingHandCursor : Qt::IBeamCursor);
|
||||
bool const clickable = buffer_view_ && buffer_view_->clickableInset();
|
||||
p->viewport()->setCursor(clickable ? Qt::PointingHandCursor
|
||||
: Qt::IBeamCursor);
|
||||
}
|
||||
|
||||
|
||||
@ -434,14 +409,14 @@ BufferView const & GuiWorkArea::bufferView() const
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::stopBlinkingCursor()
|
||||
void GuiWorkArea::stopBlinkingCaret()
|
||||
{
|
||||
d->cursor_timeout_.stop();
|
||||
d->hideCursor();
|
||||
d->caret_timeout_.stop();
|
||||
d->hideCaret();
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::startBlinkingCursor()
|
||||
void GuiWorkArea::startBlinkingCaret()
|
||||
{
|
||||
// do not show the cursor if the view is busy
|
||||
if (view().busy())
|
||||
@ -449,19 +424,28 @@ void GuiWorkArea::startBlinkingCursor()
|
||||
|
||||
Point p;
|
||||
int h = 0;
|
||||
d->buffer_view_->cursorPosAndHeight(p, h);
|
||||
d->buffer_view_->caretPosAndHeight(p, h);
|
||||
// Don't start blinking if the cursor isn't on screen.
|
||||
if (!d->buffer_view_->cursorInView(p, h))
|
||||
return;
|
||||
|
||||
d->showCursor();
|
||||
d->showCaret();
|
||||
|
||||
//we're not supposed to cache this value.
|
||||
int const time = QApplication::cursorFlashTime() / 2;
|
||||
if (time <= 0)
|
||||
return;
|
||||
d->cursor_timeout_.setInterval(time);
|
||||
d->cursor_timeout_.start();
|
||||
d->caret_timeout_.setInterval(time);
|
||||
d->caret_timeout_.start();
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::toggleCaret()
|
||||
{
|
||||
if (d->caret_visible_)
|
||||
d->hideCaret();
|
||||
else
|
||||
d->showCaret();
|
||||
}
|
||||
|
||||
|
||||
@ -481,16 +465,15 @@ void GuiWorkArea::redraw(bool update_metrics)
|
||||
d->buffer_view_->cursor().fixIfBroken();
|
||||
}
|
||||
|
||||
// update cursor position, because otherwise it has to wait until
|
||||
// update caret position, because otherwise it has to wait until
|
||||
// the blinking interval is over
|
||||
if (d->cursor_visible_) {
|
||||
d->hideCursor();
|
||||
d->showCursor();
|
||||
if (d->caret_visible_) {
|
||||
d->hideCaret();
|
||||
d->showCaret();
|
||||
}
|
||||
|
||||
LYXERR(Debug::WORKAREA, "WorkArea::redraw screen");
|
||||
d->updateScreen();
|
||||
update(0, 0, viewport()->width(), viewport()->height());
|
||||
viewport()->update();
|
||||
|
||||
/// \warning: scrollbar updating *must* be done after the BufferView is drawn
|
||||
/// because \c BufferView::updateScrollbar() is called in \c BufferView::draw().
|
||||
@ -524,9 +507,9 @@ void GuiWorkArea::processKeySym(KeySymbol const & key, KeyModifier mod)
|
||||
}
|
||||
|
||||
// In order to avoid bad surprise in the middle of an operation,
|
||||
// we better stop the blinking cursor...
|
||||
// the cursor gets restarted in GuiView::restartCursor()
|
||||
stopBlinkingCursor();
|
||||
// we better stop the blinking caret...
|
||||
// the caret gets restarted in GuiView::restartCaret()
|
||||
stopBlinkingCaret();
|
||||
guiApp->processKeySym(key, mod);
|
||||
}
|
||||
|
||||
@ -544,9 +527,9 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd)
|
||||
cmd.action() != LFUN_MOUSE_MOTION || cmd.button() != mouse_button::none;
|
||||
|
||||
// In order to avoid bad surprise in the middle of an operation, we better stop
|
||||
// the blinking cursor.
|
||||
// the blinking caret.
|
||||
if (notJustMovingTheMouse)
|
||||
p->stopBlinkingCursor();
|
||||
p->stopBlinkingCaret();
|
||||
|
||||
buffer_view_->mouseEventDispatch(cmd);
|
||||
|
||||
@ -566,8 +549,8 @@ void GuiWorkArea::Private::dispatch(FuncRequest const & cmd)
|
||||
// FIXME: let GuiView take care of those.
|
||||
lyx_view_->clearMessage();
|
||||
|
||||
// Show the cursor immediately after any operation
|
||||
p->startBlinkingCursor();
|
||||
// Show the caret immediately after any operation
|
||||
p->startBlinkingCaret();
|
||||
}
|
||||
|
||||
updateCursorShape();
|
||||
@ -578,18 +561,18 @@ void GuiWorkArea::Private::resizeBufferView()
|
||||
{
|
||||
// WARNING: Please don't put any code that will trigger a repaint here!
|
||||
// We are already inside a paint event.
|
||||
p->stopBlinkingCursor();
|
||||
p->stopBlinkingCaret();
|
||||
// Warn our container (GuiView).
|
||||
p->busy(true);
|
||||
|
||||
Point point;
|
||||
int h = 0;
|
||||
buffer_view_->cursorPosAndHeight(point, h);
|
||||
bool const cursor_in_view = buffer_view_->cursorInView(point, h);
|
||||
buffer_view_->caretPosAndHeight(point, h);
|
||||
bool const caret_in_view = buffer_view_->cursorInView(point, h);
|
||||
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
|
||||
if (cursor_in_view)
|
||||
if (caret_in_view)
|
||||
buffer_view_->scrollToCursor();
|
||||
updateScreen();
|
||||
p->viewport()->update();
|
||||
|
||||
// Update scrollbars which might have changed due different
|
||||
// BufferView dimension. This is especially important when the
|
||||
@ -599,23 +582,23 @@ void GuiWorkArea::Private::resizeBufferView()
|
||||
|
||||
need_resize_ = false;
|
||||
p->busy(false);
|
||||
// Eventually, restart the cursor after the resize event.
|
||||
// Eventually, restart the caret after the resize event.
|
||||
// We might be resizing even if the focus is on another widget so we only
|
||||
// restart the cursor if we have the focus.
|
||||
// restart the caret if we have the focus.
|
||||
if (p->hasFocus())
|
||||
QTimer::singleShot(50, p, SLOT(startBlinkingCursor()));
|
||||
QTimer::singleShot(50, p, SLOT(startBlinkingCaret()));
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::showCursor()
|
||||
void GuiWorkArea::Private::showCaret()
|
||||
{
|
||||
if (cursor_visible_)
|
||||
if (caret_visible_)
|
||||
return;
|
||||
|
||||
Point p;
|
||||
Point point;
|
||||
int h = 0;
|
||||
buffer_view_->cursorPosAndHeight(p, h);
|
||||
if (!buffer_view_->cursorInView(p, h))
|
||||
buffer_view_->caretPosAndHeight(point, h);
|
||||
if (!buffer_view_->cursorInView(point, h))
|
||||
return;
|
||||
|
||||
// RTL or not RTL
|
||||
@ -632,40 +615,45 @@ void GuiWorkArea::Private::showCursor()
|
||||
if (realfont.language() == latex_language)
|
||||
l_shape = false;
|
||||
|
||||
// show cursor on screen
|
||||
// show caret on screen
|
||||
Cursor & cur = buffer_view_->cursor();
|
||||
bool completable = cur.inset().showCompletionCursor()
|
||||
&& completer_->completionAvailable()
|
||||
&& !completer_->popupVisible()
|
||||
&& !completer_->inlineVisible();
|
||||
cursor_visible_ = true;
|
||||
cursor_->recomputeWidth();
|
||||
caret_visible_ = true;
|
||||
|
||||
//int cur_x = buffer_view_->getPos(cur).x_;
|
||||
// We may have decided to slide the cursor row so that cursor
|
||||
// We may have decided to slide the cursor row so that caret
|
||||
// is visible.
|
||||
p.x_ -= buffer_view_->horizScrollOffset();
|
||||
point.x_ -= buffer_view_->horizScrollOffset();
|
||||
|
||||
showCursor(p.x_, p.y_, h, l_shape, isrtl, completable);
|
||||
caret_->update(point.x_, point.y_, h, l_shape, isrtl, completable);
|
||||
|
||||
if (schedule_redraw_) {
|
||||
// This happens when a graphic conversion is finished. As we don't know
|
||||
// the size of the new graphics, it's better the update everything.
|
||||
// We can't use redraw() here because this would trigger a infinite
|
||||
// recursive loop with showCaret().
|
||||
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
|
||||
p->viewport()->update();
|
||||
updateScrollbar();
|
||||
schedule_redraw_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
p->viewport()->update(caret_->rect());
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::hideCursor()
|
||||
void GuiWorkArea::Private::hideCaret()
|
||||
{
|
||||
if (!cursor_visible_)
|
||||
if (!caret_visible_)
|
||||
return;
|
||||
|
||||
cursor_visible_ = false;
|
||||
removeCursor();
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::toggleCursor()
|
||||
{
|
||||
if (d->cursor_visible_)
|
||||
d->hideCursor();
|
||||
else
|
||||
d->showCursor();
|
||||
caret_visible_ = false;
|
||||
//if (!qApp->focusWidget())
|
||||
p->viewport()->update(caret_->rect());
|
||||
}
|
||||
|
||||
|
||||
@ -688,7 +676,7 @@ void GuiWorkArea::Private::updateScrollbar()
|
||||
|
||||
void GuiWorkArea::scrollTo(int value)
|
||||
{
|
||||
stopBlinkingCursor();
|
||||
stopBlinkingCaret();
|
||||
d->buffer_view_->scrollDocView(value, true);
|
||||
|
||||
if (lyxrc.cursor_follows_scrollbar) {
|
||||
@ -696,8 +684,8 @@ void GuiWorkArea::scrollTo(int value)
|
||||
// FIXME: let GuiView take care of those.
|
||||
d->lyx_view_->updateLayoutList();
|
||||
}
|
||||
// Show the cursor immediately after any operation.
|
||||
startBlinkingCursor();
|
||||
// Show the caret immediately after any operation.
|
||||
startBlinkingCaret();
|
||||
// FIXME QT5
|
||||
#ifdef Q_WS_X11
|
||||
QApplication::syncX();
|
||||
@ -803,7 +791,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e)
|
||||
d->lyx_view_->currentWorkArea()->bufferView().buffer().updateBuffer();
|
||||
}
|
||||
|
||||
startBlinkingCursor();
|
||||
startBlinkingCaret();
|
||||
QAbstractScrollArea::focusInEvent(e);
|
||||
}
|
||||
|
||||
@ -811,7 +799,7 @@ void GuiWorkArea::focusInEvent(QFocusEvent * e)
|
||||
void GuiWorkArea::focusOutEvent(QFocusEvent * e)
|
||||
{
|
||||
LYXERR(Debug::DEBUG, "GuiWorkArea::focusOutEvent(): " << this << endl);
|
||||
stopBlinkingCursor();
|
||||
stopBlinkingCaret();
|
||||
QAbstractScrollArea::focusOutEvent(e);
|
||||
}
|
||||
|
||||
@ -1158,157 +1146,31 @@ void GuiWorkArea::resizeEvent(QResizeEvent * ev)
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::update(int x, int y, int w, int h)
|
||||
void GuiWorkArea::Private::paintPreeditText(GuiPainter & pain)
|
||||
{
|
||||
p->viewport()->update(x, y, w, h);
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::paintEvent(QPaintEvent * ev)
|
||||
{
|
||||
QRectF const rc = ev->rect();
|
||||
// LYXERR(Debug::PAINTING, "paintEvent begin: x: " << rc.x()
|
||||
// << " y: " << rc.y() << " w: " << rc.width() << " h: " << rc.height());
|
||||
|
||||
if (d->needResize()) {
|
||||
d->resetScreen();
|
||||
d->resizeBufferView();
|
||||
if (d->cursor_visible_) {
|
||||
d->hideCursor();
|
||||
d->showCursor();
|
||||
}
|
||||
}
|
||||
|
||||
QPainter pain(viewport());
|
||||
double const pr = pixelRatio();
|
||||
QRectF const rcs = QRectF(rc.x() * pr, rc.y() * pr, rc.width() * pr, rc.height() * pr);
|
||||
|
||||
if (lyxrc.use_qimage) {
|
||||
QImage const & image = static_cast<QImage const &>(*d->screen_);
|
||||
pain.drawImage(rc, image, rcs);
|
||||
} else {
|
||||
QPixmap const & pixmap = static_cast<QPixmap const &>(*d->screen_);
|
||||
pain.drawPixmap(rc, pixmap, rcs);
|
||||
}
|
||||
d->cursor_->draw(pain);
|
||||
ev->accept();
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::updateScreen()
|
||||
{
|
||||
GuiPainter pain(screen_, p->pixelRatio());
|
||||
buffer_view_->draw(pain);
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::showCursor(int x, int y, int h,
|
||||
bool l_shape, bool rtl, bool completable)
|
||||
{
|
||||
if (schedule_redraw_) {
|
||||
// This happens when a graphic conversion is finished. As we don't know
|
||||
// the size of the new graphics, it's better the update everything.
|
||||
// We can't use redraw() here because this would trigger a infinite
|
||||
// recursive loop with showCursor().
|
||||
buffer_view_->resize(p->viewport()->width(), p->viewport()->height());
|
||||
updateScreen();
|
||||
updateScrollbar();
|
||||
p->viewport()->update(QRect(0, 0, p->viewport()->width(), p->viewport()->height()));
|
||||
schedule_redraw_ = false;
|
||||
// Show the cursor immediately after the update.
|
||||
hideCursor();
|
||||
p->toggleCursor();
|
||||
if (preedit_string_.empty())
|
||||
return;
|
||||
}
|
||||
|
||||
cursor_->update(x, y, h, l_shape, rtl, completable);
|
||||
cursor_->show();
|
||||
p->viewport()->update(cursor_->rect());
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::Private::removeCursor()
|
||||
{
|
||||
cursor_->hide();
|
||||
//if (!qApp->focusWidget())
|
||||
p->viewport()->update(cursor_->rect());
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
|
||||
{
|
||||
QString const & commit_string = e->commitString();
|
||||
docstring const & preedit_string
|
||||
= qstring_to_ucs4(e->preeditString());
|
||||
|
||||
if (!commit_string.isEmpty()) {
|
||||
|
||||
LYXERR(Debug::KEY, "preeditString: " << e->preeditString()
|
||||
<< " commitString: " << e->commitString());
|
||||
|
||||
int key = 0;
|
||||
|
||||
// FIXME Iwami 04/01/07: we should take care also of UTF16 surrogates here.
|
||||
for (int i = 0; i != commit_string.size(); ++i) {
|
||||
QKeyEvent ev(QEvent::KeyPress, key, Qt::NoModifier, commit_string[i]);
|
||||
keyPressEvent(&ev);
|
||||
}
|
||||
}
|
||||
|
||||
// Hide the cursor during the kana-kanji transformation.
|
||||
if (preedit_string.empty())
|
||||
startBlinkingCursor();
|
||||
else
|
||||
stopBlinkingCursor();
|
||||
|
||||
// last_width : for checking if last preedit string was/wasn't empty.
|
||||
// FIXME THREAD && FIXME
|
||||
// We could have more than one work area, right?
|
||||
static bool last_width = false;
|
||||
if (!last_width && preedit_string.empty()) {
|
||||
// if last_width is last length of preedit string.
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
GuiPainter pain(d->screen_, pixelRatio());
|
||||
d->buffer_view_->updateMetrics();
|
||||
d->buffer_view_->draw(pain);
|
||||
// FIXME: shall we use real_current_font here? (see #10478)
|
||||
FontInfo font = d->buffer_view_->cursor().getFont().fontInfo();
|
||||
FontInfo const font = buffer_view_->cursor().getFont().fontInfo();
|
||||
FontMetrics const & fm = theFontMetrics(font);
|
||||
int height = fm.maxHeight();
|
||||
int cur_x = d->cursor_->rect().left();
|
||||
int cur_y = d->cursor_->rect().bottom();
|
||||
|
||||
// redraw area of preedit string.
|
||||
update(0, cur_y - height, viewport()->width(),
|
||||
(height + 1) * d->preedit_lines_);
|
||||
|
||||
if (preedit_string.empty()) {
|
||||
last_width = false;
|
||||
d->preedit_lines_ = 1;
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
last_width = true;
|
||||
|
||||
// att : stores an IM attribute.
|
||||
QList<QInputMethodEvent::Attribute> const & att = e->attributes();
|
||||
int const height = fm.maxHeight();
|
||||
int cur_x = caret_->rect().left();
|
||||
int cur_y = caret_->rect().bottom();
|
||||
|
||||
// get attributes of input method cursor.
|
||||
// cursor_pos : cursor position in preedit string.
|
||||
size_t cursor_pos = 0;
|
||||
bool cursor_is_visible = false;
|
||||
for (int i = 0; i != att.size(); ++i) {
|
||||
if (att.at(i).type == QInputMethodEvent::Cursor) {
|
||||
cursor_pos = att.at(i).start;
|
||||
cursor_is_visible = att.at(i).length != 0;
|
||||
for (auto const & attr : preedit_attr_) {
|
||||
if (attr.type == QInputMethodEvent::Cursor) {
|
||||
cursor_pos = attr.start;
|
||||
cursor_is_visible = attr.length != 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t preedit_length = preedit_string.length();
|
||||
size_t const preedit_length = preedit_string_.length();
|
||||
|
||||
// get position of selection in input method.
|
||||
// FIXME: isn't there a way to do this simplier?
|
||||
@ -1317,12 +1179,12 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
|
||||
// rLength : selected string length in IM.
|
||||
size_t rLength = 0;
|
||||
if (cursor_pos < preedit_length) {
|
||||
for (int i = 0; i != att.size(); ++i) {
|
||||
if (att.at(i).type == QInputMethodEvent::TextFormat) {
|
||||
if (att.at(i).start <= int(cursor_pos)
|
||||
&& int(cursor_pos) < att.at(i).start + att.at(i).length) {
|
||||
rStart = att.at(i).start;
|
||||
rLength = att.at(i).length;
|
||||
for (auto const & attr : preedit_attr_) {
|
||||
if (attr.type == QInputMethodEvent::TextFormat) {
|
||||
if (attr.start <= int(cursor_pos)
|
||||
&& int(cursor_pos) < attr.start + attr.length) {
|
||||
rStart = attr.start;
|
||||
rLength = attr.length;
|
||||
if (!cursor_is_visible)
|
||||
cursor_pos += rLength;
|
||||
break;
|
||||
@ -1335,20 +1197,20 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
|
||||
rLength = 0;
|
||||
}
|
||||
|
||||
int const right_margin = d->buffer_view_->rightMargin();
|
||||
int const right_margin = buffer_view_->rightMargin();
|
||||
Painter::preedit_style ps;
|
||||
// Most often there would be only one line:
|
||||
d->preedit_lines_ = 1;
|
||||
preedit_lines_ = 1;
|
||||
for (size_t pos = 0; pos != preedit_length; ++pos) {
|
||||
char_type const typed_char = preedit_string[pos];
|
||||
char_type const typed_char = preedit_string_[pos];
|
||||
// reset preedit string style
|
||||
ps = Painter::preedit_default;
|
||||
|
||||
// if we reached the right extremity of the screen, go to next line.
|
||||
if (cur_x + fm.width(typed_char) > viewport()->width() - right_margin) {
|
||||
if (cur_x + fm.width(typed_char) > p->viewport()->width() - right_margin) {
|
||||
cur_x = right_margin;
|
||||
cur_y += height + 1;
|
||||
++d->preedit_lines_;
|
||||
++preedit_lines_;
|
||||
}
|
||||
// preedit strings are displayed with dashed underline
|
||||
// and partial strings are displayed white on black indicating
|
||||
@ -1367,11 +1229,78 @@ void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
|
||||
// draw one character and update cur_x.
|
||||
cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
|
||||
}
|
||||
}
|
||||
|
||||
// update the preedit string screen area.
|
||||
update(0, cur_y - d->preedit_lines_*height, viewport()->width(),
|
||||
|
||||
void GuiWorkArea::paintEvent(QPaintEvent * ev)
|
||||
{
|
||||
// LYXERR(Debug::PAINTING, "paintEvent begin: x: " << rc.x()
|
||||
// << " y: " << rc.y() << " w: " << rc.width() << " h: " << rc.height());
|
||||
|
||||
if (d->needResize()) {
|
||||
d->resizeBufferView();
|
||||
if (d->caret_visible_) {
|
||||
d->hideCaret();
|
||||
d->showCaret();
|
||||
}
|
||||
}
|
||||
|
||||
GuiPainter pain(viewport(), pixelRatio());
|
||||
d->buffer_view_->draw(pain, d->caret_visible_);
|
||||
|
||||
// The preedit text, if needed
|
||||
d->paintPreeditText(pain);
|
||||
|
||||
// and the caret
|
||||
if (d->caret_visible_)
|
||||
d->caret_->draw(pain);
|
||||
ev->accept();
|
||||
}
|
||||
|
||||
|
||||
void GuiWorkArea::inputMethodEvent(QInputMethodEvent * e)
|
||||
{
|
||||
LYXERR(Debug::KEY, "preeditString: " << e->preeditString()
|
||||
<< " commitString: " << e->commitString());
|
||||
|
||||
// insert the processed text in the document (handles undo)
|
||||
if (!e->commitString().isEmpty()) {
|
||||
d->buffer_view_->cursor().beginUndoGroup();
|
||||
d->buffer_view_->cursor().insert(qstring_to_ucs4(e->commitString()));
|
||||
d->buffer_view_->updateMetrics();
|
||||
d->buffer_view_->cursor().endUndoGroup();
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
// Hide the caret during the test transformation.
|
||||
if (e->preeditString().isEmpty())
|
||||
startBlinkingCaret();
|
||||
else
|
||||
stopBlinkingCaret();
|
||||
|
||||
if (d->preedit_string_.empty() && e->preeditString().isEmpty()) {
|
||||
// Nothing to do
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// The preedit text and its attributes will be used in paintPreeditText
|
||||
d->preedit_string_ = qstring_to_ucs4(e->preeditString());
|
||||
d->preedit_attr_ = e->attributes();
|
||||
|
||||
|
||||
// redraw area of preedit string.
|
||||
int height = d->caret_->rect().height();
|
||||
int cur_y = d->caret_->rect().bottom();
|
||||
viewport()->update(0, cur_y - height, viewport()->width(),
|
||||
(height + 1) * d->preedit_lines_);
|
||||
|
||||
if (d->preedit_string_.empty()) {
|
||||
d->preedit_lines_ = 1;
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't forget to accept the event!
|
||||
e->accept();
|
||||
}
|
||||
@ -1384,12 +1313,12 @@ QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
|
||||
// this is the CJK-specific composition window position and
|
||||
// the context menu position when the menu key is pressed.
|
||||
case Qt::ImMicroFocus:
|
||||
cur_r = d->cursor_->rect();
|
||||
cur_r = d->caret_->rect();
|
||||
if (d->preedit_lines_ != 1)
|
||||
cur_r.moveLeft(10);
|
||||
cur_r.moveBottom(cur_r.bottom()
|
||||
+ cur_r.height() * (d->preedit_lines_ - 1));
|
||||
// return lower right of cursor in LyX.
|
||||
// return lower right of caret in LyX.
|
||||
return cur_r;
|
||||
default:
|
||||
return QWidget::inputMethodQuery(query);
|
||||
@ -1511,7 +1440,7 @@ QSize EmbeddedWorkArea::sizeHint () const
|
||||
|
||||
void EmbeddedWorkArea::disable()
|
||||
{
|
||||
stopBlinkingCursor();
|
||||
stopBlinkingCaret();
|
||||
if (view().currentWorkArea() != this)
|
||||
return;
|
||||
// No problem if currentMainWorkArea() is 0 (setCurrentWorkArea()
|
||||
|
@ -26,10 +26,6 @@ class QDropEvent;
|
||||
class QToolButton;
|
||||
class QWidget;
|
||||
|
||||
#ifdef CursorShape
|
||||
#undef CursorShape
|
||||
#endif
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Buffer;
|
||||
@ -83,8 +79,6 @@ public:
|
||||
///
|
||||
GuiCompleter & completer();
|
||||
|
||||
Qt::CursorShape cursorShape() const;
|
||||
|
||||
/// Return the GuiView this workArea belongs to
|
||||
GuiView const & view() const;
|
||||
GuiView & view();
|
||||
@ -94,9 +88,9 @@ public:
|
||||
|
||||
public Q_SLOTS:
|
||||
///
|
||||
void stopBlinkingCursor();
|
||||
void stopBlinkingCaret();
|
||||
///
|
||||
void startBlinkingCursor();
|
||||
void startBlinkingCaret();
|
||||
|
||||
Q_SIGNALS:
|
||||
///
|
||||
@ -115,8 +109,8 @@ private Q_SLOTS:
|
||||
void scrollTo(int value);
|
||||
/// timer to limit triple clicks
|
||||
void doubleClickTimeout();
|
||||
/// toggle the cursor's visibility
|
||||
void toggleCursor();
|
||||
/// toggle the caret's visibility
|
||||
void toggleCaret();
|
||||
/// close this work area.
|
||||
/// Slot for Buffer::closing signal.
|
||||
void close();
|
||||
|
@ -13,30 +13,13 @@
|
||||
#define WORKAREA_PRIVATE_H
|
||||
|
||||
#include "FuncRequest.h"
|
||||
#include "LyXRC.h"
|
||||
|
||||
#include "support/FileName.h"
|
||||
#include "support/Timeout.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QTimer>
|
||||
|
||||
class QContextMenuEvent;
|
||||
class QDragEnterEvent;
|
||||
class QDropEvent;
|
||||
class QKeyEvent;
|
||||
class QPaintEvent;
|
||||
class QResizeEvent;
|
||||
class QToolButton;
|
||||
class QWheelEvent;
|
||||
class QWidget;
|
||||
|
||||
#ifdef CursorShape
|
||||
#undef CursorShape
|
||||
#endif
|
||||
|
||||
namespace lyx {
|
||||
|
||||
class Buffer;
|
||||
@ -44,6 +27,7 @@ class Buffer;
|
||||
namespace frontend {
|
||||
|
||||
class GuiCompleter;
|
||||
class GuiPainter;
|
||||
class GuiView;
|
||||
class GuiWorkArea;
|
||||
|
||||
@ -86,92 +70,68 @@ public:
|
||||
/**
|
||||
* Implementation of the work area (buffer view GUI)
|
||||
*/
|
||||
class CursorWidget;
|
||||
class CaretWidget;
|
||||
|
||||
struct GuiWorkArea::Private
|
||||
{
|
||||
///
|
||||
Private(GuiWorkArea *);
|
||||
|
||||
/// update the passed area.
|
||||
void update(int x, int y, int w, int h);
|
||||
///
|
||||
void updateScreen();
|
||||
~Private();
|
||||
|
||||
///
|
||||
void resizeBufferView();
|
||||
|
||||
/// paint the cursor and store the background
|
||||
void showCursor(int x, int y, int h,
|
||||
bool l_shape, bool rtl, bool completable);
|
||||
|
||||
/// hide the cursor
|
||||
void removeCursor();
|
||||
///
|
||||
void dispatch(FuncRequest const & cmd0);
|
||||
/// hide the visible cursor, if it is visible
|
||||
void hideCursor();
|
||||
/// show the cursor if it is not visible
|
||||
void showCursor();
|
||||
/// hide the visible caret, if it is visible
|
||||
void hideCaret();
|
||||
/// show the caret if it is not visible
|
||||
void showCaret();
|
||||
/// Set the range and value of the scrollbar and connect to its valueChanged
|
||||
/// signal.
|
||||
void updateScrollbar();
|
||||
/// Change the cursor when the mouse hovers over a clickable inset
|
||||
void updateCursorShape();
|
||||
///
|
||||
void setCursorShape(Qt::CursorShape shape);
|
||||
|
||||
void paintPreeditText(GuiPainter & pain);
|
||||
|
||||
bool needResize() const {
|
||||
return need_resize_ || p->pixelRatio() != pixel_ratio_;
|
||||
}
|
||||
|
||||
void resetScreen()
|
||||
{
|
||||
delete screen_;
|
||||
pixel_ratio_ = p->pixelRatio();
|
||||
if (lyxrc.use_qimage) {
|
||||
QImage *x =
|
||||
new QImage(static_cast<int>(pixel_ratio_ * p->viewport()->width()),
|
||||
static_cast<int>(pixel_ratio_ * p->viewport()->height()),
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
#if QT_VERSION >= 0x050000
|
||||
x->setDevicePixelRatio(pixel_ratio_);
|
||||
#endif
|
||||
screen_ = x;
|
||||
} else {
|
||||
QPixmap *x =
|
||||
new QPixmap(static_cast<int>(pixel_ratio_ * p->viewport()->width()),
|
||||
static_cast<int>(pixel_ratio_ * p->viewport()->height()));
|
||||
#if QT_VERSION >= 0x050000
|
||||
x->setDevicePixelRatio(pixel_ratio_);
|
||||
#endif
|
||||
screen_ = x;
|
||||
}
|
||||
}
|
||||
///
|
||||
GuiWorkArea * p;
|
||||
///
|
||||
QPaintDevice * screen_;
|
||||
///
|
||||
BufferView * buffer_view_;
|
||||
///
|
||||
GuiView * lyx_view_;
|
||||
/// is the cursor currently displayed
|
||||
bool cursor_visible_;
|
||||
|
||||
///
|
||||
QTimer cursor_timeout_;
|
||||
CaretWidget * caret_;
|
||||
/// is the caret currently displayed
|
||||
bool caret_visible_;
|
||||
///
|
||||
QTimer caret_timeout_;
|
||||
|
||||
///
|
||||
SyntheticMouseEvent synthetic_mouse_event_;
|
||||
///
|
||||
DoubleClick dc_event_;
|
||||
|
||||
///
|
||||
CursorWidget * cursor_;
|
||||
///
|
||||
bool need_resize_;
|
||||
///
|
||||
bool schedule_redraw_;
|
||||
///
|
||||
|
||||
/// the current preedit text of the input method
|
||||
docstring preedit_string_;
|
||||
/// Number of lines used by preedit text
|
||||
int preedit_lines_;
|
||||
/// the attributes of the preedit text
|
||||
QList<QInputMethodEvent::Attribute> preedit_attr_;
|
||||
|
||||
/// Ratio between physical pixels and device-independent pixels
|
||||
/// We save the last used value to detect changes of the
|
||||
/// current pixel_ratio of the viewport.
|
||||
|
@ -231,25 +231,17 @@ void InsetDisplayLabelBox::metrics(MetricsInfo & mi, Dimension & dim) const
|
||||
{
|
||||
InsetLabelBox::metrics(mi, dim);
|
||||
if (!parent_.editing(mi.base.bv)
|
||||
&& parent_.cell(parent_.displayIdx()).empty()) {
|
||||
dim.wid = 0;
|
||||
dim.asc = 0;
|
||||
dim.des = 0;
|
||||
}
|
||||
&& parent_.cell(parent_.displayIdx()).empty())
|
||||
dim.clear();
|
||||
}
|
||||
|
||||
|
||||
void InsetDisplayLabelBox::draw(PainterInfo & pi, int x, int y) const
|
||||
{
|
||||
if (parent_.editing(pi.base.bv)
|
||||
|| !parent_.cell(parent_.displayIdx()).empty()) {
|
||||
InsetLabelBox::draw(pi, x, y);
|
||||
} else {
|
||||
bool enabled = pi.pain.isDrawingEnabled();
|
||||
pi.pain.setDrawingEnabled(false);
|
||||
InsetLabelBox::draw(pi, x, y);
|
||||
pi.pain.setDrawingEnabled(enabled);
|
||||
}
|
||||
|| !parent_.cell(parent_.displayIdx()).empty()
|
||||
|| pi.pain.isNull())
|
||||
InsetLabelBox::draw(pi, x, y);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user