Resolves: tdf#143449 implement a blinking cursor for weldeditview
Take a snapshot of what's under the cursor first and keep it, draw the cursor and take another snapshot. Then timer just calls an invalidate on the cursor area with the cursor reason for the invalidation, and just swap those snapshots on each draw while that's the only invalidation reason. Change-Id: I49a26af292905556cd6033f814aa8175c0da69a4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199435 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
#include <editeng/editeng.hxx>
|
||||
#include <editeng/editview.hxx>
|
||||
#include <vcl/outdev.hxx>
|
||||
#include <vcl/timer.hxx>
|
||||
#include <vcl/virdev.hxx>
|
||||
#include <vcl/weld/customweld.hxx>
|
||||
|
||||
class WeldEditAccessible;
|
||||
@@ -55,6 +57,14 @@ protected:
|
||||
std::unique_ptr<EditView> m_xEditView;
|
||||
rtl::Reference<WeldEditAccessible> m_xAccessible;
|
||||
|
||||
// Cursor blink support
|
||||
AutoTimer m_aCursorTimer;
|
||||
bool m_bCursorVisible;
|
||||
tools::Rectangle m_aCachedCursorPixRect; // pixel coords of cached cursor area
|
||||
ScopedVclPtrInstance<VirtualDevice> m_xCursorOnDev;
|
||||
ScopedVclPtrInstance<VirtualDevice> m_xCursorOffDev;
|
||||
DECL_DLLPRIVATE_LINK(BlinkTimerHdl, Timer*, void);
|
||||
|
||||
virtual void makeEditEngine();
|
||||
|
||||
void InitAccessible();
|
||||
|
||||
@@ -99,7 +99,22 @@ void WeldEditView::Paste()
|
||||
|
||||
WeldEditView::WeldEditView()
|
||||
: m_bAcceptsTab(false)
|
||||
, m_aCursorTimer("WeldEditView CursorTimer")
|
||||
, m_bCursorVisible(false)
|
||||
{
|
||||
m_aCursorTimer.SetInvokeHandler(LINK(this, WeldEditView, BlinkTimerHdl));
|
||||
}
|
||||
|
||||
IMPL_LINK_NOARG(WeldEditView, BlinkTimerHdl, Timer*, void)
|
||||
{
|
||||
m_bCursorVisible = !m_bCursorVisible;
|
||||
if (!m_aCachedCursorPixRect.IsEmpty())
|
||||
{
|
||||
OutputDevice& rDevice = EditViewOutputDevice();
|
||||
Invalidate(rDevice.PixelToLogic(m_aCachedCursorPixRect), weld::InvalidateFlags::Cursor);
|
||||
}
|
||||
else
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
// tdf#127033 want to use UI font so override makeEditEngine to enable that
|
||||
@@ -228,6 +243,21 @@ void WeldEditView::DoPaint(vcl::RenderContext& rRenderContext, const tools::Rect
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path: blink-only repaint with valid cache
|
||||
weld::InvalidateFlags ePending = GetInvalidateFlags();
|
||||
if (ePending == weld::InvalidateFlags::Cursor && !m_aCachedCursorPixRect.IsEmpty())
|
||||
{
|
||||
VirtualDevice& rSrc = m_bCursorVisible ? *m_xCursorOnDev : *m_xCursorOffDev;
|
||||
// Blit cached bitmap in pixel coordinates to avoid rounding issues
|
||||
bool bMapMode = rRenderContext.IsMapModeEnabled();
|
||||
rRenderContext.EnableMapMode(false);
|
||||
rRenderContext.DrawOutDev(m_aCachedCursorPixRect.TopLeft(),
|
||||
m_aCachedCursorPixRect.GetSize(), Point(0, 0),
|
||||
m_aCachedCursorPixRect.GetSize(), rSrc);
|
||||
rRenderContext.EnableMapMode(bMapMode);
|
||||
return;
|
||||
}
|
||||
|
||||
auto popIt = rRenderContext.ScopedPush(vcl::PushFlags::ALL);
|
||||
rRenderContext.SetClipRegion();
|
||||
|
||||
@@ -239,7 +269,45 @@ void WeldEditView::DoPaint(vcl::RenderContext& rRenderContext, const tools::Rect
|
||||
{
|
||||
pEditView->ShowCursor(false);
|
||||
vcl::Cursor* pCursor = pEditView->GetCursor();
|
||||
pCursor->DrawToDevice(rRenderContext);
|
||||
|
||||
// Get the pixel bounding rect the cursor will occupy
|
||||
tools::Rectangle aPixRect = pCursor->GetBoundRect(rRenderContext);
|
||||
if (!aPixRect.IsEmpty())
|
||||
{
|
||||
m_aCachedCursorPixRect = aPixRect;
|
||||
Size aPixSize = aPixRect.GetSize();
|
||||
|
||||
// Cache in pixel coordinates to avoid rounding issues
|
||||
bool bMapMode = rRenderContext.IsMapModeEnabled();
|
||||
rRenderContext.EnableMapMode(false);
|
||||
|
||||
// Cache "cursor off" — text without cursor (before drawing cursor)
|
||||
m_xCursorOffDev->SetOutputSizePixel(aPixSize);
|
||||
m_xCursorOffDev->SetMapMode(MapMode(MapUnit::MapPixel));
|
||||
m_xCursorOffDev->DrawOutDev(Point(0, 0), aPixSize, aPixRect.TopLeft(), aPixSize,
|
||||
rRenderContext);
|
||||
|
||||
// Draw cursor, then cache "cursor on"
|
||||
rRenderContext.EnableMapMode(bMapMode);
|
||||
pCursor->DrawToDevice(rRenderContext);
|
||||
rRenderContext.EnableMapMode(false);
|
||||
|
||||
m_xCursorOnDev->SetOutputSizePixel(aPixSize);
|
||||
m_xCursorOnDev->SetMapMode(MapMode(MapUnit::MapPixel));
|
||||
m_xCursorOnDev->DrawOutDev(Point(0, 0), aPixSize, aPixRect.TopLeft(), aPixSize,
|
||||
rRenderContext);
|
||||
|
||||
rRenderContext.EnableMapMode(bMapMode);
|
||||
|
||||
// If cursor should be hidden, restore to clean state
|
||||
if (!m_bCursorVisible)
|
||||
{
|
||||
rRenderContext.EnableMapMode(false);
|
||||
rRenderContext.DrawOutDev(aPixRect.TopLeft(), aPixSize, Point(0, 0), aPixSize,
|
||||
*m_xCursorOffDev);
|
||||
rRenderContext.EnableMapMode(bMapMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get logic selection
|
||||
@@ -1573,6 +1641,16 @@ void WeldEditView::GetFocus()
|
||||
if (pEditView)
|
||||
{
|
||||
pEditView->ShowCursor(false);
|
||||
|
||||
m_bCursorVisible = true;
|
||||
m_aCachedCursorPixRect = tools::Rectangle();
|
||||
sal_uInt64 nBlinkTime = Application::GetSettings().GetStyleSettings().GetCursorBlinkTime();
|
||||
if (nBlinkTime != STYLE_CURSOR_NOBLINKTIME)
|
||||
{
|
||||
m_aCursorTimer.SetTimeout(nBlinkTime);
|
||||
m_aCursorTimer.Start();
|
||||
}
|
||||
|
||||
Invalidate(); // redraw with cursor
|
||||
}
|
||||
|
||||
@@ -1591,6 +1669,10 @@ void WeldEditView::GetFocus()
|
||||
|
||||
void WeldEditView::LoseFocus()
|
||||
{
|
||||
m_aCursorTimer.Stop();
|
||||
m_bCursorVisible = false;
|
||||
m_aCachedCursorPixRect = tools::Rectangle();
|
||||
|
||||
weld::CustomWidgetController::LoseFocus();
|
||||
Invalidate(); // redraw without cursor
|
||||
|
||||
|
||||
Reference in New Issue
Block a user