aseprite/src/ui/tooltips.cpp

320 lines
7.5 KiB
C++

// ASEPRITE gui library
// Copyright (C) 2001-2012 David Capello
//
// This source file is ditributed under a BSD-like license, please
// read LICENSE.txt for more information.
#include "config.h"
#include <allegro.h>
#include <string>
#include "base/unique_ptr.h"
#include "gfx/size.h"
#include "ui/graphics.h"
#include "ui/gui.h"
#include "ui/intern.h"
#include "ui/paint_event.h"
#include "ui/preferred_size_event.h"
static const int kTooltipDelayMsecs = 300;
using namespace gfx;
namespace ui {
TooltipManager::TooltipManager()
: Widget(JI_WIDGET)
{
Manager* manager = Manager::getDefault();
manager->addMessageFilter(JM_MOUSEENTER, this);
manager->addMessageFilter(JM_KEYPRESSED, this);
manager->addMessageFilter(JM_BUTTONPRESSED, this);
manager->addMessageFilter(JM_MOUSELEAVE, this);
setVisible(false);
}
TooltipManager::~TooltipManager()
{
Manager* manager = Manager::getDefault();
manager->removeMessageFilterFor(this);
}
void TooltipManager::addTooltipFor(Widget* widget, const char* text, int arrowAlign)
{
m_tips[widget] = TipInfo(text, arrowAlign);
}
bool TooltipManager::onProcessMessage(Message* msg)
{
switch (msg->type) {
case JM_MOUSEENTER: {
UI_FOREACH_WIDGET(*msg->any.widgets, itWidget) {
Tips::iterator it = m_tips.find(*itWidget);
if (it != m_tips.end()) {
m_target.widget = it->first;
m_target.tipInfo = it->second;
if (m_timer == NULL) {
m_timer.reset(new Timer(kTooltipDelayMsecs, this));
m_timer->Tick.connect(&TooltipManager::onTick, this);
}
m_timer->start();
}
}
return false;
}
case JM_KEYPRESSED:
case JM_BUTTONPRESSED:
case JM_MOUSELEAVE:
if (m_tipWindow) {
m_tipWindow->closeWindow(NULL);
m_tipWindow.reset();
}
if (m_timer)
m_timer->stop();
return false;
}
return Widget::onProcessMessage(msg);
}
void TooltipManager::onTick()
{
if (!m_tipWindow) {
m_tipWindow.reset(new TipWindow(m_target.tipInfo.text.c_str(), true));
gfx::Rect bounds = m_target.widget->getBounds();
int x = jmouse_x(0)+12*jguiscale();
int y = jmouse_y(0)+12*jguiscale();
int w, h;
m_tipWindow->setArrowAlign(m_target.tipInfo.arrowAlign);
m_tipWindow->remap_window();
w = jrect_w(m_tipWindow->rc);
h = jrect_h(m_tipWindow->rc);
switch (m_target.tipInfo.arrowAlign) {
case JI_TOP | JI_LEFT:
x = bounds.x + bounds.w;
y = bounds.y + bounds.h;
break;
case JI_TOP | JI_RIGHT:
x = bounds.x - w;
y = bounds.y + bounds.h;
break;
case JI_BOTTOM | JI_LEFT:
x = bounds.x + bounds.w;
y = bounds.y - h;
break;
case JI_BOTTOM | JI_RIGHT:
x = bounds.x - w;
y = bounds.y - h;
break;
case JI_TOP:
x = bounds.x + bounds.w/2 - w/2;
y = bounds.y + bounds.h;
break;
case JI_BOTTOM:
x = bounds.x + bounds.w/2 - w/2;
y = bounds.y - h;
break;
case JI_LEFT:
x = bounds.x + bounds.w;
y = bounds.y + bounds.h/2 - h/2;
break;
case JI_RIGHT:
x = bounds.x - w;
y = bounds.y + bounds.h/2 - h/2;
break;
}
// if (x+w > JI_SCREEN_W) {
// x = jmouse_x(0) - w - 4*jguiscale();
// y = jmouse_y(0);
// }
m_tipWindow->position_window(MID(0, x, JI_SCREEN_W-w),
MID(0, y, JI_SCREEN_H-h));
m_tipWindow->openWindow();
}
m_timer->stop();
}
// TipWindow
TipWindow::TipWindow(const char *text, bool close_on_buttonpressed)
: Window(false, text)
{
m_close_on_buttonpressed = close_on_buttonpressed;
m_hot_region = NULL;
m_filtering = false;
m_arrowAlign = 0;
set_sizeable(false);
set_moveable(false);
set_wantfocus(false);
setAlign(JI_LEFT | JI_TOP);
removeDecorativeWidgets();
initTheme();
}
TipWindow::~TipWindow()
{
if (m_filtering) {
m_filtering = false;
getManager()->removeMessageFilter(JM_MOTION, this);
getManager()->removeMessageFilter(JM_BUTTONPRESSED, this);
getManager()->removeMessageFilter(JM_KEYPRESSED, this);
}
if (m_hot_region != NULL) {
jregion_free(m_hot_region);
}
}
/**
* @param region The new hot-region. This pointer is holded by the @a widget.
* So you can't destroy it after calling this routine.
*/
void TipWindow::set_hotregion(JRegion region)
{
ASSERT(region != NULL);
if (m_hot_region != NULL)
jregion_free(m_hot_region);
if (!m_filtering) {
m_filtering = true;
getManager()->addMessageFilter(JM_MOTION, this);
getManager()->addMessageFilter(JM_BUTTONPRESSED, this);
getManager()->addMessageFilter(JM_KEYPRESSED, this);
}
m_hot_region = region;
}
int TipWindow::getArrowAlign() const
{
return m_arrowAlign;
}
void TipWindow::setArrowAlign(int arrowAlign)
{
m_arrowAlign = arrowAlign;
}
bool TipWindow::onProcessMessage(Message* msg)
{
switch (msg->type) {
case JM_CLOSE:
if (m_filtering) {
m_filtering = false;
getManager()->removeMessageFilter(JM_MOTION, this);
getManager()->removeMessageFilter(JM_BUTTONPRESSED, this);
getManager()->removeMessageFilter(JM_KEYPRESSED, this);
}
break;
case JM_MOUSELEAVE:
if (m_hot_region == NULL)
this->closeWindow(NULL);
break;
case JM_KEYPRESSED:
if (m_filtering && msg->key.scancode < KEY_MODIFIERS)
this->closeWindow(NULL);
break;
case JM_BUTTONPRESSED:
/* if the user click outside the window, we have to close the
tooltip window */
if (m_filtering) {
Widget* picked = this->pick(msg->mouse.x, msg->mouse.y);
if (!picked || picked->getRoot() != this) {
this->closeWindow(NULL);
}
}
/* this is used when the user click inside a small text
tooltip */
if (m_close_on_buttonpressed)
this->closeWindow(NULL);
break;
case JM_MOTION:
if (m_hot_region != NULL &&
getManager()->getCapture() == NULL) {
struct jrect box;
/* if the mouse is outside the hot-region we have to close the window */
if (!jregion_point_in(m_hot_region,
msg->mouse.x, msg->mouse.y, &box)) {
this->closeWindow(NULL);
}
}
break;
}
return Window::onProcessMessage(msg);
}
void TipWindow::onPreferredSize(PreferredSizeEvent& ev)
{
ScreenGraphics g;
g.setFont(getFont());
Size resultSize = g.fitString(getText(),
(getClientBounds() - getBorder()).w,
getAlign());
resultSize.w += this->border_width.l + this->border_width.r;
resultSize.h += this->border_width.t + this->border_width.b;
if (!getChildren().empty()) {
Size maxSize(0, 0);
Size reqSize;
UI_FOREACH_WIDGET(getChildren(), it) {
Widget* child = *it;
reqSize = child->getPreferredSize();
maxSize.w = MAX(maxSize.w, reqSize.w);
maxSize.h = MAX(maxSize.h, reqSize.h);
}
resultSize.w = MAX(resultSize.w, border_width.l + maxSize.w + border_width.r);
resultSize.h += maxSize.h;
}
ev.setPreferredSize(resultSize);
}
void TipWindow::onInitTheme(InitThemeEvent& ev)
{
Window::onInitTheme(ev);
this->border_width.l = 6 * jguiscale();
this->border_width.t = 6 * jguiscale();
this->border_width.r = 6 * jguiscale();
this->border_width.b = 7 * jguiscale();
// Setup the background color.
setBgColor(makecol(255, 255, 200));
}
void TipWindow::onPaint(PaintEvent& ev)
{
getTheme()->paintTooltip(ev);
}
} // namespace ui