tdf#148349 add a way to call the user's attention to a widget

Change-Id: I2846155a44f3e51ddd8cc1acd81e84a38b4d3934
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133030
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
This commit is contained in:
Caolán McNamara 2022-04-14 20:13:17 +01:00
parent fbfda267e8
commit d1da1c59d1
5 changed files with 201 additions and 11 deletions

View File

@ -320,6 +320,9 @@ public:
virtual VclPtr<VirtualDevice> create_virtual_device() const = 0;
//do something transient to attract the attention of the user to the widget
virtual void call_attention_to() = 0;
//make this widget look like a page in a notebook
virtual void set_stack_background() = 0;
//make this widget look like it has a highlighted background

View File

@ -295,22 +295,29 @@ IMPL_LINK(FindTextFieldControl, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
}
}
}
// Select text in the search box when Ctrl-F pressed
else if ( bMod1 && nCode == KEY_F )
m_xWidget->select_entry_region(0, -1);
// Execute the search when Ctrl-G, F3 and Shift-RETURN pressed (in addition to ActivateHdl condition which handles bare RETURN)
else if ( (bMod1 && KEY_G == nCode) || (bShift && KEY_RETURN == nCode) || (KEY_F3 == nCode) )
{
ActivateFind(bShift);
bRet = true;
}
else
{
auto awtKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyEvent.GetKeyCode());
const OUString aCommand(m_pAcc->findCommand(awtKey));
if (aCommand == ".uno:SearchDialog")
// Select text in the search box when Ctrl-F pressed
if ( bMod1 && nCode == KEY_F )
m_xWidget->select_entry_region(0, -1);
// Execute the search when Ctrl-G, F3 and Shift-RETURN pressed (in addition to ActivateHdl condition which handles bare RETURN)
else if ( (bMod1 && KEY_G == nCode) || (bShift && KEY_RETURN == nCode) || (KEY_F3 == nCode) )
{
ActivateFind(bShift);
bRet = true;
}
else if (aCommand == ".uno:SearchDialog")
bRet = m_pAcc->execute(awtKey);
// find-shortcut called with focus already in find
if (aCommand == "vnd.sun.star.findbar:FocusToFindbar")
{
m_xWidget->call_attention_to();
bRet = true;
}
}
return bRet || ChildKeyInput(rKeyEvent);

View File

@ -167,10 +167,13 @@ public:
virtual ~SalInstanceMenu() override;
};
class SalFlashAttention;
class SalInstanceWidget : public virtual weld::Widget
{
protected:
VclPtr<vcl::Window> m_xWidget;
std::unique_ptr<SalFlashAttention> m_xFlashAttention;
SalInstanceBuilder* m_pBuilder;
private:
@ -367,6 +370,8 @@ public:
virtual void get_property_tree(tools::JsonWriter& rJsonWriter) override;
virtual void call_attention_to() override;
virtual void set_stack_background() override;
virtual void set_title_background() override;
@ -1000,6 +1005,8 @@ public:
virtual void HandleEventListener(VclWindowEvent& rEvent) override;
virtual void call_attention_to() override;
virtual ~SalInstanceComboBoxWithEdit() override;
};

View File

@ -559,6 +559,79 @@ VclPtr<VirtualDevice> SalInstanceWidget::create_virtual_device() const
DeviceFormat::DEFAULT);
}
class SalFlashAttention
{
private:
VclPtr<vcl::Window> m_xWidget;
Timer m_aFlashTimer;
Color m_aOrigControlBackground;
Wallpaper m_aOrigBackground;
bool m_bOrigControlBackground;
int m_nFlashCount;
void SetFlash()
{
Color aColor(Application::GetSettings().GetStyleSettings().GetHighlightColor());
m_xWidget->SetControlBackground(aColor);
}
void ClearFlash()
{
if (m_bOrigControlBackground)
m_xWidget->SetControlBackground(m_aOrigControlBackground);
else
m_xWidget->SetControlBackground();
}
void Flash()
{
constexpr int FlashesWanted = 1;
if (m_nFlashCount % 2 == 0)
ClearFlash();
else
SetFlash();
if (m_nFlashCount == FlashesWanted * 2)
return;
++m_nFlashCount;
m_aFlashTimer.Start();
}
DECL_LINK(FlashTimeout, Timer*, void);
public:
SalFlashAttention(VclPtr<vcl::Window> xWidget)
: m_xWidget(xWidget)
, m_aFlashTimer("SalFlashAttention")
, m_bOrigControlBackground(false)
, m_nFlashCount(1)
{
m_aFlashTimer.SetTimeout(150);
m_aFlashTimer.SetInvokeHandler(LINK(this, SalFlashAttention, FlashTimeout));
}
void Start()
{
m_bOrigControlBackground = m_xWidget->IsControlBackground();
if (m_bOrigControlBackground)
m_aOrigControlBackground = m_xWidget->GetControlBackground();
m_aFlashTimer.Start();
}
~SalFlashAttention() { ClearFlash(); }
};
IMPL_LINK_NOARG(SalFlashAttention, FlashTimeout, Timer*, void) { Flash(); }
void SalInstanceWidget::call_attention_to()
{
m_xFlashAttention.reset(new SalFlashAttention(m_xWidget));
m_xFlashAttention->Start();
}
css::uno::Reference<css::datatransfer::dnd::XDropTarget> SalInstanceWidget::get_drop_target()
{
return m_xWidget->GetDropTarget();
@ -6356,6 +6429,14 @@ SalInstanceComboBoxWithEdit::SalInstanceComboBoxWithEdit(::ComboBox* pComboBox,
bool SalInstanceComboBoxWithEdit::has_entry() const { return true; }
void SalInstanceComboBoxWithEdit::call_attention_to()
{
Edit* pEdit = m_xComboBox->GetSubEdit();
assert(pEdit);
m_xFlashAttention.reset(new SalFlashAttention(pEdit));
m_xFlashAttention->Start();
}
bool SalInstanceComboBoxWithEdit::changed_by_direct_pick() const
{
return m_bInSelect && !m_xComboBox->IsModifyByKeyboard() && !m_xComboBox->IsTravelSelect();

View File

@ -2494,6 +2494,92 @@ void set_buildable_id(GtkBuildable* pWidget, const OString& rId)
namespace {
class FlashAttention
{
private:
GtkWidget* m_pWidget;
int m_nFlashCount;
gint m_nFlashTimeout;
static gboolean signalDraw(GtkWidget* pWidget, cairo_t* cr, gpointer self)
{
FlashAttention* pThis = static_cast<FlashAttention*>(self);
if (pThis->m_nFlashCount % 2 == 0)
return false;
GtkAllocation alloc {0, 0,
gtk_widget_get_allocated_width(pWidget),
gtk_widget_get_allocated_height(pWidget)};
Color aColor(Application::GetSettings().GetStyleSettings().GetHighlightColor());
cairo_set_source_rgba(cr, aColor.GetRed() / 255.0, aColor.GetGreen() / 255.0, aColor.GetBlue() / 255.0, 0.5);
cairo_rectangle(cr, alloc.x + 0.5, alloc.y + 0.5, alloc.width - 1, alloc.height - 1);
cairo_fill(cr);
return false;
}
static void signalUnmap(gpointer self)
{
FlashAttention* pThis = static_cast<FlashAttention*>(self);
pThis->ClearFlash();
}
void ClearFlash()
{
if (m_nFlashTimeout != 0)
{
g_source_remove(m_nFlashTimeout);
m_nFlashTimeout = 0;
}
if (m_pWidget)
{
gtk_widget_queue_draw(m_pWidget);
g_signal_handlers_disconnect_by_func(m_pWidget, reinterpret_cast<void*>(signalDraw), this);
g_signal_handlers_disconnect_by_func(m_pWidget, reinterpret_cast<void*>(signalUnmap), this);
m_pWidget = nullptr;
}
}
bool QueueFlash()
{
constexpr int FlashesWanted = 1;
gtk_widget_queue_draw(m_pWidget);
m_nFlashCount++;
if (m_nFlashCount == FlashesWanted * 2)
{
ClearFlash();
return false;
}
return true;
}
static gboolean FlashTimeout(FlashAttention* pThis)
{
return pThis->QueueFlash();
}
public:
FlashAttention(GtkWidget* pWidget)
: m_pWidget(pWidget)
, m_nFlashCount(1)
{
g_signal_connect_after(m_pWidget, "draw", G_CALLBACK(signalDraw), this);
g_signal_connect_swapped(m_pWidget, "unmap", G_CALLBACK(signalUnmap), this);
gtk_widget_queue_draw(m_pWidget);
m_nFlashTimeout = g_timeout_add(250, reinterpret_cast<GSourceFunc>(FlashTimeout), this);
}
~FlashAttention()
{
ClearFlash();
}
};
class GtkInstanceWidget : public virtual weld::Widget
{
protected:
@ -2789,6 +2875,7 @@ private:
#if !GTK_CHECK_VERSION(4, 0, 0)
GdkDragAction m_eDragAction;
#endif
std::unique_ptr<FlashAttention> m_xFlashAttention;
gulong m_nFocusInSignalId;
gulong m_nMnemonicActivateSignalId;
gulong m_nFocusOutSignalId;
@ -4153,6 +4240,11 @@ public:
//not implemented for the gtk variant
}
virtual void call_attention_to() override
{
m_xFlashAttention.reset(new FlashAttention(m_pWidget));
}
virtual void set_stack_background() override
{
do_set_background(Application::GetSettings().GetStyleSettings().GetWindowColor());