Improve StatusBar tooltips, add app.tip

This commit is contained in:
Christian Kaiser 2025-07-31 19:56:31 -03:00
parent 6d89a6bc15
commit 25371727df
4 changed files with 59 additions and 6 deletions

View File

@ -34,10 +34,10 @@
#include "app/tools/tool_loop_manager.h" #include "app/tools/tool_loop_manager.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/context_bar.h" #include "app/ui/context_bar.h"
#include "app/ui/doc_view.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
#include "app/ui/editor/tool_loop_impl.h" #include "app/ui/editor/tool_loop_impl.h"
#include "app/ui/main_window.h" #include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h" #include "app/ui/timeline/timeline.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "base/fs.h" #include "base/fs.h"
@ -498,6 +498,30 @@ int App_useTool(lua_State* L)
return 0; return 0;
} }
int App_tip(lua_State* L)
{
const auto* ctx = App::instance()->context();
if (!ctx || !ctx->isUIAvailable() || !StatusBar::instance())
return 0; // No UI to show the tooltip
if (!lua_isstring(L, 1))
return luaL_error(L, "app.tip() message parameter must be a string");
const std::string message = lua_tostring(L, 1);
if (message.empty())
return luaL_error(L, "app.tip() message cannot be empty");
int duration = 2000;
if (lua_isinteger(L, 2)) {
const int durationInput = lua_tointeger(L, 2);
if (durationInput > 0 && durationInput < 30000) // Reasonable limits
duration = lua_tointeger(L, 2);
}
StatusBar::instance()->showTip(duration, message);
return 0;
}
int App_get_events(lua_State* L) int App_get_events(lua_State* L)
{ {
push_app_events(L); push_app_events(L);
@ -820,6 +844,7 @@ const luaL_Reg App_methods[] = {
{ "alert", App_alert }, { "alert", App_alert },
{ "refresh", App_refresh }, { "refresh", App_refresh },
{ "useTool", App_useTool }, { "useTool", App_useTool },
{ "tip", App_tip },
{ nullptr, nullptr } { nullptr, nullptr }
}; };

View File

@ -549,7 +549,10 @@ private:
class StatusBar::CustomizedTipWindow : public ui::TipWindow { class StatusBar::CustomizedTipWindow : public ui::TipWindow {
public: public:
CustomizedTipWindow(const std::string& text) : ui::TipWindow(text) {} CustomizedTipWindow(const std::string& text) : ui::TipWindow(text)
{
setClickBehavior(ClickBehavior::CloseOnClick);
}
void setInterval(int msecs) void setInterval(int msecs)
{ {
@ -557,9 +560,11 @@ public:
m_timer.reset(new ui::Timer(msecs, this)); m_timer.reset(new ui::Timer(msecs, this));
else else
m_timer->setInterval(msecs); m_timer->setInterval(msecs);
m_originalInterval = msecs;
} }
void startTimer() { m_timer->start(); } void startTimer() const { m_timer->start(); }
int originalInterval() const { return m_originalInterval; }
protected: protected:
bool onProcessMessage(Message* msg) override bool onProcessMessage(Message* msg) override
@ -569,12 +574,18 @@ protected:
closeWindow(nullptr); closeWindow(nullptr);
m_timer->stop(); m_timer->stop();
break; break;
case kMouseEnterMessage: m_timer->stop(); break;
case kMouseLeaveMessage:
m_timer->setInterval(m_originalInterval);
m_timer->start();
return true; // Avoid PopupWindow closing us.
} }
return ui::TipWindow::onProcessMessage(msg); return ui::TipWindow::onProcessMessage(msg);
} }
private: private:
std::unique_ptr<ui::Timer> m_timer; std::unique_ptr<ui::Timer> m_timer;
int m_originalInterval = 0;
}; };
// TODO Use a ui::TipWindow with rounded borders, when we add support // TODO Use a ui::TipWindow with rounded borders, when we add support
@ -797,12 +808,21 @@ bool StatusBar::setStatusText(int msecs, const std::string& msg)
void StatusBar::showTip(int msecs, const std::string& msg) void StatusBar::showTip(int msecs, const std::string& msg)
{ {
ASSERT(msecs > 0); ASSERT(msecs > 0);
if (m_tipwindow == NULL) { if (m_tipwindow == NULL) {
m_tipwindow = new CustomizedTipWindow(msg); m_tipwindow = new CustomizedTipWindow(msg);
} }
else { else {
m_tipwindow->setText(msg); if (m_tipwindow->isVisible() && m_tipwindow->text() != msg) {
// If the tip window is still visible, append the text on top so
// the old message is still readable (unless it's the exact same text)
m_tipwindow->setText(m_tipwindow->text() + "\n" + msg);
// Always use the longest interval to give the user more time to read.
msecs = m_tipwindow->originalInterval() > msecs ? m_tipwindow->originalInterval() : msecs;
}
else {
m_tipwindow->setText(msg);
}
} }
m_tipwindow->setInterval(msecs); m_tipwindow->setInterval(msecs);

View File

@ -157,6 +157,13 @@ bool PopupWindow::onProcessMessage(Message* msg)
} }
break; break;
} }
case ClickBehavior::CloseOnClick: {
if (bounds().contains(mouseMsg->position())) {
closeWindow(nullptr);
}
break;
}
} }
} }
break; break;

View File

@ -18,7 +18,8 @@ public:
enum class ClickBehavior { enum class ClickBehavior {
DoNothingOnClick, DoNothingOnClick,
CloseOnClickInOtherWindow, CloseOnClickInOtherWindow,
CloseOnClickOutsideHotRegion CloseOnClickOutsideHotRegion,
CloseOnClick
}; };
enum class EnterBehavior { enum class EnterBehavior {