mirror of
https://github.com/aseprite/aseprite.git
synced 2024-10-05 22:39:47 +00:00
384 lines
9.0 KiB
C++
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
|