mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-25 01:43:36 +00:00
391 lines
9.0 KiB
C++
391 lines
9.0 KiB
C++
// 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.
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "ui/button.h"
|
|
#include "ui/manager.h"
|
|
#include "ui/message.h"
|
|
#include "ui/size_hint_event.h"
|
|
#include "ui/theme.h"
|
|
#include "ui/widget.h"
|
|
#include "ui/window.h"
|
|
|
|
#include <queue>
|
|
#include <cstring>
|
|
|
|
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)
|
|
{
|
|
setAlign(CENTER | MIDDLE);
|
|
setText(text);
|
|
setFocusStop(true);
|
|
|
|
// Initialize theme
|
|
setType(m_drawType); // TODO Fix this nasty trick
|
|
initTheme();
|
|
setType(type);
|
|
}
|
|
|
|
ButtonBase::~ButtonBase()
|
|
{
|
|
if (m_iconInterface)
|
|
m_iconInterface->destroy();
|
|
}
|
|
|
|
WidgetType ButtonBase::behaviorType() const
|
|
{
|
|
return m_behaviorType;
|
|
}
|
|
|
|
WidgetType ButtonBase::drawType() 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 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();
|
|
}
|
|
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()) &&
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kKeyUpMessage:
|
|
if (isEnabled()) {
|
|
if (m_behaviorType == kButtonWidget) {
|
|
if (isSelected()) {
|
|
generateButtonSelectSignal();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kMouseDownMessage:
|
|
switch (m_behaviorType) {
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
break;
|
|
|
|
case kMouseEnterMessage:
|
|
case kMouseLeaveMessage:
|
|
// TODO theme stuff
|
|
if (isEnabled())
|
|
invalidate();
|
|
break;
|
|
}
|
|
|
|
return Widget::onProcessMessage(msg);
|
|
}
|
|
|
|
void ButtonBase::onSizeHint(SizeHintEvent& ev)
|
|
{
|
|
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);
|
|
|
|
ev.setSizeHint(box.w + border().width(),
|
|
box.h + border().height());
|
|
}
|
|
|
|
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);
|
|
|
|
// Fire onClick() event
|
|
Event ev(this);
|
|
onClick(ev);
|
|
}
|
|
|
|
// ======================================================================
|
|
// Button class
|
|
// ======================================================================
|
|
|
|
Button::Button(const std::string& text)
|
|
: ButtonBase(text, kButtonWidget, kButtonWidget, kButtonWidget)
|
|
{
|
|
setAlign(CENTER | MIDDLE);
|
|
}
|
|
|
|
// ======================================================================
|
|
// CheckBox class
|
|
// ======================================================================
|
|
|
|
CheckBox::CheckBox(const std::string& text, WidgetType drawType)
|
|
: ButtonBase(text, kCheckWidget, kCheckWidget, drawType)
|
|
{
|
|
setAlign(LEFT | MIDDLE);
|
|
}
|
|
|
|
// ======================================================================
|
|
// RadioButton class
|
|
// ======================================================================
|
|
|
|
RadioButton::RadioButton(const std::string& text, int radioGroup, WidgetType drawType)
|
|
: ButtonBase(text, kRadioWidget, kRadioWidget, drawType)
|
|
{
|
|
setAlign(LEFT | 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 = 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RadioButton::onSelect(bool selected)
|
|
{
|
|
ButtonBase::onSelect(selected);
|
|
if (!selected)
|
|
return;
|
|
|
|
if (!m_handleSelect)
|
|
return;
|
|
|
|
if (behaviorType() == kRadioWidget) {
|
|
deselectRadioGroup();
|
|
|
|
m_handleSelect = false;
|
|
setSelected(true);
|
|
m_handleSelect = true;
|
|
}
|
|
}
|
|
|
|
} // namespace ui
|