aseprite/src/ui/button.cpp

391 lines
9.0 KiB
C++
Raw Normal View History

// Aseprite UI Library
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
2007-09-19 07:57:02 +08:00
#ifdef HAVE_CONFIG_H
2009-07-13 04:29:16 +08:00
#include "config.h"
#endif
2009-07-13 04:29:16 +08:00
2012-06-18 09:49:58 +08:00
#include "ui/button.h"
#include "ui/manager.h"
#include "ui/message.h"
2015-12-04 08:50:05 +08:00
#include "ui/size_hint_event.h"
2012-06-18 09:49:58 +08:00
#include "ui/theme.h"
#include "ui/widget.h"
2012-07-09 10:24:42 +08:00
#include "ui/window.h"
#include <queue>
#include <cstring>
2007-09-19 07:57:02 +08:00
namespace ui {
ButtonBase::ButtonBase(const std::string& text,
WidgetType type,
WidgetType behaviorType,
WidgetType drawType)
: Widget(type)
, m_pressedStatus(false)
, m_behaviorType(behaviorType)
, m_drawType(drawType)
, m_iconInterface(NULL)
, m_handleSelect(true)
2007-09-19 07:57:02 +08:00
{
setAlign(CENTER | MIDDLE);
setText(text);
setFocusStop(true);
// Initialize theme
setType(m_drawType); // TODO Fix this nasty trick
initTheme();
setType(type);
2007-09-19 07:57:02 +08:00
}
ButtonBase::~ButtonBase()
2007-09-19 07:57:02 +08:00
{
if (m_iconInterface)
m_iconInterface->destroy();
2007-09-19 07:57:02 +08:00
}
WidgetType ButtonBase::behaviorType() const
2007-09-19 07:57:02 +08:00
{
return m_behaviorType;
2007-09-19 07:57:02 +08:00
}
WidgetType ButtonBase::drawType() const
2007-09-19 07:57:02 +08:00
{
return m_drawType;
2007-09-19 07:57:02 +08:00
}
void ButtonBase::setIconInterface(IButtonIcon* iconInterface)
2007-09-19 07:57:02 +08:00
{
if (m_iconInterface)
m_iconInterface->destroy();
2007-09-19 07:57:02 +08:00
m_iconInterface = iconInterface;
2007-09-19 07:57:02 +08:00
invalidate();
2007-09-19 07:57:02 +08:00
}
void ButtonBase::onClick(Event& ev)
2007-09-19 07:57:02 +08:00
{
// Fire Click() signal
Click(ev);
2007-09-19 07:57:02 +08:00
}
bool ButtonBase::onProcessMessage(Message* msg)
2007-09-19 07:57:02 +08:00
{
switch (msg->type()) {
2007-09-19 07:57:02 +08:00
case kFocusEnterMessage:
case kFocusLeaveMessage:
if (isEnabled()) {
if (m_behaviorType == kButtonWidget) {
// Deselect the widget (maybe the user press the key, but
// before release it, changes the focus).
if (isSelected())
setSelected(false);
}
// TODO theme specific stuff
invalidate();
2007-09-19 07:57:02 +08:00
}
break;
case kKeyDownMessage: {
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
KeyScancode scancode = keymsg->scancode();
// If the button is enabled.
if (isEnabled()) {
bool mnemonicPressed =
((msg->altPressed() || msg->cmdPressed()) &&
2016-11-25 06:26:21 +08:00
mnemonicCharPressed(keymsg));
// For kButtonWidget
if (m_behaviorType == kButtonWidget) {
// Has focus and press enter/space
if (hasFocus()) {
if ((scancode == kKeyEnter) ||
(scancode == kKeyEnterPad) ||
(scancode == kKeySpace)) {
setSelected(true);
return true;
}
}
// Check if the user pressed mnemonic.
if (mnemonicPressed) {
setSelected(true);
return true;
}
// Magnetic widget catches ENTERs
else if (isFocusMagnet() &&
((scancode == kKeyEnter) ||
(scancode == kKeyEnterPad))) {
manager()->setFocus(this);
// Dispatch focus movement messages (because the buttons
// process them)
manager()->dispatchMessages();
setSelected(true);
return true;
}
}
// For kCheckWidget or kRadioWidget
else {
/* if the widget has the focus and the user press space or
if the user press Alt+the underscored letter of the button */
if ((hasFocus() && (scancode == kKeySpace)) || mnemonicPressed) {
if (m_behaviorType == kCheckWidget) {
// Swap the select status
setSelected(!isSelected());
invalidate();
}
else if (m_behaviorType == kRadioWidget) {
if (!isSelected()) {
setSelected(true);
}
}
return true;
}
}
2007-09-19 07:57:02 +08:00
}
break;
}
2007-09-19 07:57:02 +08:00
case kKeyUpMessage:
if (isEnabled()) {
if (m_behaviorType == kButtonWidget) {
if (isSelected()) {
generateButtonSelectSignal();
return true;
}
}
2007-09-19 07:57:02 +08:00
}
break;
case kMouseDownMessage:
switch (m_behaviorType) {
2007-09-19 07:57:02 +08:00
case kButtonWidget:
if (isEnabled()) {
setSelected(true);
m_pressedStatus = isSelected();
captureMouse();
}
return true;
case kCheckWidget:
if (isEnabled()) {
setSelected(!isSelected());
m_pressedStatus = isSelected();
captureMouse();
}
return true;
case kRadioWidget:
if (isEnabled()) {
if (!isSelected()) {
m_handleSelect = false;
setSelected(true);
m_handleSelect = true;
m_pressedStatus = isSelected();
captureMouse();
}
}
return true;
2007-09-19 07:57:02 +08:00
}
break;
case kMouseUpMessage:
if (hasCapture()) {
releaseMouse();
if (hasMouseOver()) {
switch (m_behaviorType) {
case kButtonWidget:
generateButtonSelectSignal();
break;
case kCheckWidget:
{
// Fire onClick() event
Event ev(this);
onClick(ev);
invalidate();
}
break;
case kRadioWidget:
{
setSelected(false);
setSelected(true);
// Fire onClick() event
Event ev(this);
onClick(ev);
}
break;
}
}
return true;
2007-09-19 07:57:02 +08:00
}
break;
case kMouseMoveMessage:
if (isEnabled() && hasCapture()) {
bool hasMouse = hasMouseOver();
m_handleSelect = false;
// Switch state when the mouse go out
if ((hasMouse && isSelected() != m_pressedStatus) ||
(!hasMouse && isSelected() == m_pressedStatus)) {
if (hasMouse)
setSelected(m_pressedStatus);
else
setSelected(!m_pressedStatus);
}
m_handleSelect = true;
2007-09-19 07:57:02 +08:00
}
break;
2007-09-19 07:57:02 +08:00
case kMouseEnterMessage:
case kMouseLeaveMessage:
// TODO theme stuff
if (isEnabled())
invalidate();
2007-09-19 07:57:02 +08:00
break;
}
return Widget::onProcessMessage(msg);
2007-09-19 07:57:02 +08:00
}
2015-12-04 08:50:05 +08:00
void ButtonBase::onSizeHint(SizeHintEvent& ev)
2007-09-19 07:57:02 +08:00
{
gfx::Rect box;
gfx::Size iconSize = (m_iconInterface ? m_iconInterface->size(): gfx::Size(0, 0));
getTextIconInfo(&box, NULL, NULL,
m_iconInterface ? m_iconInterface->iconAlign(): 0,
iconSize.w,
iconSize.h);
2007-09-19 07:57:02 +08:00
2015-12-04 08:50:05 +08:00
ev.setSizeHint(box.w + border().width(),
box.h + border().height());
}
2007-09-19 07:57:02 +08:00
void ButtonBase::onPaint(PaintEvent& ev)
{
switch (m_drawType) {
case kButtonWidget: theme()->paintButton(ev); break;
case kCheckWidget: theme()->paintCheckBox(ev); break;
case kRadioWidget: theme()->paintRadioButton(ev); break;
}
}
void ButtonBase::generateButtonSelectSignal()
{
// Deselect
setSelected(false);
2007-09-19 07:57:02 +08:00
// Fire onClick() event
Event ev(this);
onClick(ev);
2007-09-19 07:57:02 +08:00
}
// ======================================================================
// Button class
// ======================================================================
Button::Button(const std::string& text)
: ButtonBase(text, kButtonWidget, kButtonWidget, kButtonWidget)
2007-09-19 07:57:02 +08:00
{
setAlign(CENTER | MIDDLE);
}
2007-09-19 07:57:02 +08:00
// ======================================================================
// CheckBox class
// ======================================================================
2007-09-19 07:57:02 +08:00
CheckBox::CheckBox(const std::string& text, WidgetType drawType)
: ButtonBase(text, kCheckWidget, kCheckWidget, drawType)
{
setAlign(LEFT | MIDDLE);
}
2007-09-19 07:57:02 +08:00
// ======================================================================
// RadioButton class
// ======================================================================
2007-09-19 07:57:02 +08:00
RadioButton::RadioButton(const std::string& text, int radioGroup, WidgetType drawType)
: ButtonBase(text, kRadioWidget, kRadioWidget, drawType)
{
setAlign(LEFT | MIDDLE);
setRadioGroup(radioGroup);
}
2007-09-19 07:57:02 +08:00
void RadioButton::setRadioGroup(int radioGroup)
{
m_radioGroup = radioGroup;
// TODO: Update old and new groups
}
int RadioButton::getRadioGroup() const
{
return m_radioGroup;
}
void RadioButton::deselectRadioGroup()
{
Widget* widget = window();
if (!widget)
return;
std::queue<Widget*> allChildrens;
allChildrens.push(widget);
while (!allChildrens.empty()) {
widget = allChildrens.front();
allChildrens.pop();
if (RadioButton* radioButton = dynamic_cast<RadioButton*>(widget)) {
if (radioButton->getRadioGroup() == m_radioGroup)
radioButton->setSelected(false);
}
for (auto child : widget->children()) {
allChildrens.push(child);
2007-09-19 07:57:02 +08:00
}
}
}
void RadioButton::onSelect(bool selected)
2007-09-19 07:57:02 +08:00
{
ButtonBase::onSelect(selected);
if (!selected)
return;
2007-09-19 07:57:02 +08:00
if (!m_handleSelect)
return;
2007-09-19 07:57:02 +08:00
if (behaviorType() == kRadioWidget) {
deselectRadioGroup();
m_handleSelect = false;
setSelected(true);
m_handleSelect = true;
}
2007-09-19 07:57:02 +08:00
}
} // namespace ui