#include "LyXVC.h"
#include "Text.h"
#include "TextMetrics.h"
-#include "Undo.h"
#include "version.h"
#include "support/convert.h"
#include "frontends/WorkAreaManager.h"
#include <QContextMenuEvent>
-#if (QT_VERSION < 0x050000)
-#include <QInputContext>
-#endif
#include <QDrag>
#include <QHelpEvent>
+#include <QInputMethod>
#ifdef Q_OS_MAC
#include <QProxyStyle>
#endif
double GuiWorkArea::pixelRatio() const
{
-#if QT_VERSION >= 0x050000
- return qt_scale_factor * devicePixelRatio();
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+ return devicePixelRatioF();
#else
- return 1.0;
+ return devicePixelRatio();
#endif
}
});
d->resetScreen();
- // With Qt4.5 a mouse event will happen before the first paint event
+ // 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());
// Enables input methods for asian languages.
// Must be set when creating custom text editing widgets.
setAttribute(Qt::WA_InputMethodEnabled, true);
+ // obtain transformation information to reset it when LyX gets refocus
+ d->im_item_trans_ = d->im_->inputItemTransform();
}
// Don't start blinking if the cursor isn't on screen, unless we
// are not ready to know whether the cursor is on screen.
- if (!d->buffer_view_->buffer().undo().activeUndoGroup()
- && !d->buffer_view_->caretInView())
+ if (!d->buffer_view_->busy() && !d->buffer_view_->caretInView())
return;
d->showCaret();
void GuiWorkArea::Private::resetCaret()
{
- // Don't start blinking if the cursor isn't on screen.
- if (!buffer_view_->caretInView())
+ // Don't start blinking if the cursor isn't on screen or the window
+ // does not have focus
+ if (!buffer_view_->caretInView() || !p->hasFocus())
return;
- // completion indicator
- Cursor const & cur = buffer_view_->cursor();
- bool const completable = cur.inset().showCompletionCursor()
- && completer_->completionAvailable()
- && !completer_->popupVisible()
- && !completer_->inlineVisible();
-
- buffer_view_->buildCaretGeometry(completable);
-
needs_caret_geometry_update_ = true;
caret_visible_ = true;
}
{
// we cannot update geometry if not ready and we do not need to if
// caret is not in view.
- if (buffer_view_->buffer().undo().activeUndoGroup()
- || !buffer_view_->caretInView())
+ if (buffer_view_->busy() || !buffer_view_->caretInView())
return;
+ // completion indicator
+ Cursor const & cur = buffer_view_->cursor();
+ bool const completable = cur.inset().showCompletionCursor()
+ && completer_->completionAvailable()
+ && !completer_->popupVisible()
+ && !completer_->inlineVisible();
+
+ buffer_view_->buildCaretGeometry(completable);
needs_caret_geometry_update_ = false;
}
}
// Show the caret immediately after any operation.
startBlinkingCaret();
- // FIXME QT5
-#ifdef Q_WS_X11
- QApplication::syncX();
-#endif
}
++pos.rx();
}
}
+ if (e->reason() == QContextMenuEvent::Keyboard)
+ // Subtract the top margin, see #12811
+ pos.setY(pos.y() - d->buffer_view_->topMargin());
+
name = d->buffer_view_->contextMenu(pos.x(), pos.y());
}
return;
}
-#if (QT_VERSION < 0x050000) && !defined(__HAIKU__)
- inputContext()->reset();
-#endif
-
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
FuncRequest const cmd(LFUN_MOUSE_PRESS, e->position().x(), e->position().y(),
#else
// Wheel rotation by one notch results in a delta() of 120 (see
// documentation of QWheelEvent)
// But first we have to ignore horizontal scroll events.
-#if QT_VERSION < 0x050000
- if (ev->orientation() == Qt::Horizontal) {
- ev->accept();
- return;
- }
- double const delta = ev->delta() / 120.0;
-#else
QPoint const aDelta = ev->angleDelta();
// skip horizontal wheel event
if (abs(aDelta.x()) > abs(aDelta.y())) {
return;
}
double const delta = aDelta.y() / 120.0;
-#endif
bool zoom = false;
switch (lyxrc.scroll_wheel_zoom) {
// 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();
+ int yy = pm.top();
for (--rlast; rit != rlast; ++rit) {
int h = rit->height();
if ((up && yy + h > 0)
Point point;
Dimension dim;
buffer_view_->caretPosAndDim(point, dim);
- int cur_x = point.x_;
+ int cur_x = point.x_ - dim.width();
int cur_y = point.y_ + dim.height();
+ // lower margin of the preedit area to separate the candidate window
+ // report to IM the height of preedit rectangle larger than the actual by
+ // preedit_lower_margin so that the conversion suggestion window does not
+ // hide the underline of the preedit text
+ int preedit_lower_margin = 3;
+ // reset item transformation since it gets wrong after the item get
+ // lost and regain focus.
+ im_->setInputItemTransform(im_item_trans_);
+ // force fulldraw to remove previous paint remaining on screen
+ buffer_view_->processUpdateFlags(Update::ForceDraw);
// get attributes of input method cursor.
// cursor_pos : cursor position in preedit string.
rLength = 0;
}
- int const right_margin = buffer_view_->rightMargin();
+ int const text_width = p->viewport()->width() - buffer_view_->rightMargin()
+ - buffer_view_->leftMargin();
Painter::preedit_style ps;
// Most often there would be only one line:
preedit_lines_ = 1;
ps = Painter::preedit_default;
// if we reached the right extremity of the screen, go to next line.
- if (cur_x + fm.width(typed_char) > p->viewport()->width() - right_margin) {
- cur_x = right_margin;
+ if (cur_x + fm.width(typed_char) > p->viewport()->width() - buffer_view_->rightMargin()) {
+ cur_x = buffer_view_->leftMargin();
cur_y += dim.height() + 1;
++preedit_lines_;
}
// FIXME: should be put out of the loop.
if (pos >= rStart
&& pos < rStart + rLength
- && !(cursor_pos < rLength && rLength == preedit_length))
+ && !(cursor_pos < rLength && rLength == preedit_length)) {
ps = Painter::preedit_selecting;
+ }
if (pos == cursor_pos
&& (cursor_pos < rLength && rLength == preedit_length))
// draw one character and update cur_x.
cur_x += pain.preeditText(cur_x, cur_y, typed_char, font, ps);
}
+
+ // the selection candidate window follows the position of Qt::ImCursorRectangle
+ // so we set it here in preparation for InputMethodQuery.
+ if(rLength > 0) {
+ // candidate selection mode
+ div_t mod_cur, mod_anc;
+ // width of preedit string in pixels
+ int preedit_width = fm.width(preedit_string_);
+ // calculate for modular so that its remainder and quotient become horizontal
+ // and vertical adjustment factor respectively
+ // FIXME: divider should be (text_width - font width of the end-of-line character)
+ // however, since it's very rare that preedit becomes so long that it goes over
+ // more than two lines, current error of position is more or less negligible
+ mod_cur = div(text_width + cur_x - (preedit_width - fm.width(preedit_string_.substr(0, rStart))),
+ text_width);
+ mod_anc = div(text_width + cur_x - (preedit_width - fm.width(preedit_string_.substr(0, rStart + rLength))),
+ text_width);
+ // Obtain cursor and anchor rectangles to bind starting and ending points of selection.
+ // Since the remainder of div moves from positive to negative as the line becomes longer
+ // while its quotient repeats zero twice, we need to take it into account by conditioning.
+ if (mod_cur.rem >= 0)
+ im_cursor_rect_ = QRectF(mod_cur.rem,
+ (cur_y - dim.height()) + (dim.height() + 1) * (mod_cur.quot - 1), 1,
+ dim.height() + preedit_lower_margin);
+ else
+ im_cursor_rect_ = QRectF(text_width + mod_cur.rem,
+ (cur_y - dim.height()) + (dim.height() + 1) * (mod_cur.quot - 2), 1,
+ dim.height() + preedit_lower_margin);
+ if (mod_anc.rem >= 0)
+ im_anchor_rect_ = QRectF(mod_anc.rem,
+ point.y_ + (dim.height() + 1) * (mod_anc.quot - 1), 1,
+ dim.height() + preedit_lower_margin );
+ else
+ im_anchor_rect_ = QRectF(text_width + mod_anc.rem,
+ point.y_ + (dim.height() + 1) * (mod_anc.quot - 2), 1,
+ dim.height() + preedit_lower_margin );
+ } else {
+ im_cursor_rect_ = QRectF(point.x_, point.y_, 1, dim.height() + preedit_lower_margin);
+ im_anchor_rect_ = im_cursor_rect_;
+ }
+ // Urge platform input method to make inputMethodQuery to check the values
+ // set above
+ im_->update(Qt::ImQueryInput);
}
void GuiWorkArea::Private::resetScreen()
{
if (use_backingstore_) {
- int const pr = p->pixelRatio();
- screen_ = QImage(pr * p->viewport()->width(),
- pr * p->viewport()->height(),
+ double const pr = p->pixelRatio();
+ screen_ = QImage(int(pr * p->viewport()->width()),
+ int(pr * p->viewport()->height()),
QImage::Format_ARGB32_Premultiplied);
-# if QT_VERSION >= 0x050000
screen_.setDevicePixelRatio(pr);
-# endif
}
}
// Do not trigger the painting machinery if we are not ready (see
// bug #10989). The second test triggers when in the middle of a
// dispatch operation.
- if (view().busy() || d->buffer_view_->buffer().undo().activeUndoGroup()) {
- // Since macOS has turned the screen black at this point, our
- // backing store has to be copied to screen (this is a no-op
- // except on macOS).
+ if (view().busy() || d->buffer_view_->busy()) {
+ // Since the screen may have turned black at this point, our
+ // backing store has to be copied to screen. This is a no-op
+ // except when our drawing strategy is "backingstore" (macOS,
+ // Wayland, or set in prefs).
d->updateScreen(ev->rect());
// Ignore this paint event, but request a new one for later.
viewport()->update(ev->rect());
QVariant GuiWorkArea::inputMethodQuery(Qt::InputMethodQuery query) const
{
+ LYXERR(Debug::INFO, "incoming InputMethodQuery Value: 0x" << std::hex << query);
switch (query) {
- // this is the CJK-specific composition window position and
- // the context menu position when the menu key is pressed.
-#if (QT_VERSION < 0x050000)
- case Qt::ImMicroFocus: {
-#else
+ // this is the CJK-specific composition window position and
+ // the context menu position when the menu key is pressed.
case Qt::ImCursorRectangle: {
-#endif
- CaretGeometry const & cg = bufferView().caretGeometry();
- return QRect(cg.left - 10 * (d->preedit_lines_ != 1),
- cg.top + cg.height() * d->preedit_lines_,
- cg.width(), cg.height());
+ return QVariant(d->im_cursor_rect_);
+ break;
+ }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
+ case Qt::ImAnchorRectangle: {
+ return QVariant(d->im_anchor_rect_);
+ break;
}
+#endif
default:
return QWidget::inputMethodQuery(query);
}
QObject::connect(this, SIGNAL(currentChanged(int)),
this, SLOT(on_currentTabChanged(int)));
+ // Fix for #11835
+ QObject::connect(this, SIGNAL(tabBarClicked(int)),
+ this, SLOT(on_currentTabChanged(int)));
closeBufferButton = new QToolButton(this);
closeBufferButton->setPalette(pal);
// painting of the frame of the tab widget.
// This is needed for gtk style in Qt.
QStylePainter p(this);
-#if QT_VERSION < 0x050000
- QStyleOptionTabWidgetFrameV2 opt;
-#else
QStyleOptionTabWidgetFrame opt;
-#endif
initStyleOption(&opt);
opt.rect = style()->subElementRect(QStyle::SE_TabWidgetTabPane,
&opt, this);
// show tab popup
QMenu popup;
popup.addAction(QIcon(getPixmap("images/", "hidetab", "svgz,png")),
- qt_("Hide tab"), this, SLOT(hideCurrentTab()));
+ qt_("&Hide Tab"), this, SLOT(hideCurrentTab()));
// we want to show the 'close' option only if this is not a child buffer.
Buffer const & buf = wa->bufferView().buffer();
if (!buf.parent())
popup.addAction(QIcon(getPixmap("images/", "closetab", "svgz,png")),
- qt_("Close tab"), this, SLOT(closeCurrentBuffer()));
+ qt_("&Close Tab"), this, SLOT(closeCurrentBuffer()));
popup.exec(tabBar()->mapToGlobal(pos));
clicked_tab_ = -1;
this, SLOT(updateDisplay()));
connect(reloadPB, SIGNAL(clicked()), this, SLOT(reload()));
connect(ignorePB, SIGNAL(clicked()), this, SLOT(ignore()));
- setMessageColour({notificationFrame}, {reloadPB, ignorePB});
+ setMessageColour({notificationFrame, externalModificationLabel},
+ {reloadPB, ignorePB});
updateDisplay();
}