aseprite/src/ui/button.cpp
2013-01-27 12:13:13 -03:00

384 lines
9.0 KiB
C++

// ASEPRITE gui library
// Copyright (C) 2001-2013 David Capello
//
// This source file is distributed under a BSD-like license, please
// read LICENSE.txt for more information.
#include "config.h"
#include "ui/button.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/preferred_size_event.h"
#include "ui/rect.h"
#include "ui/theme.h"
#include "ui/widget.h"
#include "ui/window.h"
#include <allegro/gfx.h>
#include <allegro/keyboard.h>
#include <allegro/timer.h>
#include <queue>
#include <string.h>
namespace ui {
ButtonBase::ButtonBase(const char* text, int type, int behaviorType, int drawType)
: Widget(type)
, m_pressedStatus(false)
, m_handleSelect(true)
, m_behaviorType(behaviorType)
, m_drawType(drawType)
, m_iconInterface(NULL)
{
setAlign(JI_CENTER | JI_MIDDLE);
setText(text);
setFocusStop(true);
// Initialize theme
this->type = m_drawType; // TODO Fix this nasty trick
initTheme();
this->type = type;
}
ButtonBase::~ButtonBase()
{
if (m_iconInterface)
m_iconInterface->destroy();
}
int ButtonBase::getBehaviorType() const
{
return m_behaviorType;
}
int ButtonBase::getDrawType() const
{
return m_drawType;
}
void ButtonBase::setIconInterface(IButtonIcon* iconInterface)
{
if (m_iconInterface)
m_iconInterface->destroy();
m_iconInterface = iconInterface;
invalidate();
}
void ButtonBase::onClick(Event& ev)
{
// Fire Click() signal
Click(ev);
}
bool ButtonBase::onProcessMessage(Message* msg)
{
switch (msg->type) {
case JM_FOCUSENTER:
case JM_FOCUSLEAVE:
if (isEnabled()) {
if (m_behaviorType == JI_BUTTON) {
// 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();
}
break;
case JM_KEYPRESSED:
// If the button is enabled.
if (isEnabled()) {
// For JI_BUTTON
if (m_behaviorType == JI_BUTTON) {
// Has focus and press enter/space
if (hasFocus()) {
if ((msg->key.scancode == KEY_ENTER) ||
(msg->key.scancode == KEY_ENTER_PAD) ||
(msg->key.scancode == KEY_SPACE)) {
setSelected(true);
return true;
}
}
// Check if the user pressed mnemonic.
if ((msg->any.shifts & KB_ALT_FLAG) &&
(isScancodeMnemonic(msg->key.scancode))) {
setSelected(true);
return true;
}
// Magnetic widget catches ENTERs
else if (isFocusMagnet() &&
((msg->key.scancode == KEY_ENTER) ||
(msg->key.scancode == KEY_ENTER_PAD))) {
getManager()->setFocus(this);
// Dispatch focus movement messages (because the buttons
// process them)
getManager()->dispatchMessages();
setSelected(true);
return true;
}
}
// For JI_CHECK or JI_RADIO
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() &&
(msg->key.scancode == KEY_SPACE)) ||
((msg->any.shifts & KB_ALT_FLAG) &&
(isScancodeMnemonic(msg->key.scancode)))) {
if (m_behaviorType == JI_CHECK) {
// Swap the select status
setSelected(!isSelected());
invalidate();
}
else if (m_behaviorType == JI_RADIO) {
if (!isSelected()) {
setSelected(true);
}
}
return true;
}
}
}
break;
case JM_KEYRELEASED:
if (isEnabled()) {
if (m_behaviorType == JI_BUTTON) {
if (isSelected()) {
generateButtonSelectSignal();
return true;
}
}
}
break;
case JM_BUTTONPRESSED:
switch (m_behaviorType) {
case JI_BUTTON:
if (isEnabled()) {
setSelected(true);
m_pressedStatus = isSelected();
captureMouse();
}
return true;
case JI_CHECK:
if (isEnabled()) {
setSelected(!isSelected());
m_pressedStatus = isSelected();
captureMouse();
}
return true;
case JI_RADIO:
if (isEnabled()) {
if (!isSelected()) {
m_handleSelect = false;
setSelected(true);
m_handleSelect = true;
m_pressedStatus = isSelected();
captureMouse();
}
}
return true;
}
break;
case JM_BUTTONRELEASED:
if (hasCapture()) {
releaseMouse();
if (hasMouseOver()) {
switch (m_behaviorType) {
case JI_BUTTON:
generateButtonSelectSignal();
break;
case JI_CHECK:
{
// Fire onClick() event
Event ev(this);
onClick(ev);
invalidate();
}
break;
case JI_RADIO:
{
setSelected(false);
setSelected(true);
// Fire onClick() event
Event ev(this);
onClick(ev);
}
break;
}
}
return true;
}
break;
case JM_MOTION:
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;
}
break;
case JM_MOUSEENTER:
case JM_MOUSELEAVE:
// TODO theme stuff
if (isEnabled())
invalidate();
break;
}
return Widget::onProcessMessage(msg);
}
void ButtonBase::onPreferredSize(PreferredSizeEvent& ev)
{
struct jrect box, text, icon;
jwidget_get_texticon_info(this, &box, &text, &icon,
m_iconInterface ? m_iconInterface->getIconAlign(): 0,
m_iconInterface ? m_iconInterface->getWidth(): 0,
m_iconInterface ? m_iconInterface->getHeight(): 0);
ev.setPreferredSize(this->border_width.l + jrect_w(&box) + this->border_width.r,
this->border_width.t + jrect_h(&box) + this->border_width.b);
}
void ButtonBase::onPaint(PaintEvent& ev)
{
switch (m_drawType) {
case JI_BUTTON: getTheme()->paintButton(ev); break;
case JI_CHECK: getTheme()->paintCheckBox(ev); break;
case JI_RADIO: getTheme()->paintRadioButton(ev); break;
}
}
void ButtonBase::generateButtonSelectSignal()
{
// Deselect
setSelected(false);
// Fire onClick() event
Event ev(this);
onClick(ev);
}
// ======================================================================
// Button class
// ======================================================================
Button::Button(const char *text)
: ButtonBase(text, JI_BUTTON, JI_BUTTON, JI_BUTTON)
{
setAlign(JI_CENTER | JI_MIDDLE);
}
// ======================================================================
// CheckBox class
// ======================================================================
CheckBox::CheckBox(const char *text, int drawType)
: ButtonBase(text, JI_CHECK, JI_CHECK, drawType)
{
setAlign(JI_LEFT | JI_MIDDLE);
}
// ======================================================================
// RadioButton class
// ======================================================================
RadioButton::RadioButton(const char *text, int radioGroup, int drawType)
: ButtonBase(text, JI_RADIO, JI_RADIO, drawType)
{
setAlign(JI_LEFT | JI_MIDDLE);
setRadioGroup(radioGroup);
}
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 = getRoot();
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);
}
UI_FOREACH_WIDGET(widget->getChildren(), it) {
allChildrens.push(*it);
}
}
}
void RadioButton::onSelect()
{
ButtonBase::onSelect();
if (!m_handleSelect)
return;
if (getBehaviorType() == JI_RADIO) {
deselectRadioGroup();
m_handleSelect = false;
setSelected(true);
m_handleSelect = true;
}
}
} // namespace ui