aseprite/src/ui/button.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

371 lines
8.7 KiB
C++
Raw Normal View History

// Aseprite UI Library
// Copyright (C) 2019-2023 Igara Studio S.A.
2018-10-11 23:01:21 +08:00
// Copyright (C) 2001-2018 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 <cstring>
2012-07-09 10:24:42 +08:00
#include <queue>
2007-09-19 07:57:02 +08:00
namespace ui {
ButtonBase::ButtonBase(const std::string& text,
2018-10-11 23:01:21 +08:00
const WidgetType type,
const WidgetType behaviorType,
const WidgetType drawType)
: Widget(type)
, m_pressedStatus(false)
, m_behaviorType(behaviorType)
, m_handleSelect(true)
2007-09-19 07:57:02 +08:00
{
setAlign(CENTER | MIDDLE);
setText(text);
setFocusStop(true);
// Initialize theme
setType(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
{
}
WidgetType ButtonBase::behaviorType() const
2007-09-19 07:57:02 +08:00
{
return m_behaviorType;
2007-09-19 07:57:02 +08:00
}
void ButtonBase::onClick()
2007-09-19 07:57:02 +08:00
{
// Fire Click() signal
Click();
2007-09-19 07:57:02 +08:00
}
void ButtonBase::onRightClick()
{
RightClick();
}
bool ButtonBase::onProcessMessage(Message* msg)
2007-09-19 07:57:02 +08:00
{
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();
2007-09-19 07:57:02 +08:00
}
break;
case kKeyDownMessage: {
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
KeyScancode scancode = keymsg->scancode();
if (isEnabled() && isVisible()) {
const bool mnemonicPressed = isMnemonicPressedWithModifiers(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;
}
}
if ( // Check if the user pressed mnemonic
mnemonicPressed ||
// Magnetic widget catches ENTERs
(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() && hasFocus()) {
switch (m_behaviorType) {
case kButtonWidget:
if (isSelected()) {
generateButtonSelectSignal();
return true;
}
break;
case kCheckWidget: {
KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
KeyScancode scancode = keymsg->scancode();
const bool mnemonicPressed = isMnemonicPressedWithModifiers(keymsg);
// Fire the onClick() event only if the user pressed space or
// Alt+the underscored letter of the checkbox label.
if (scancode == kKeySpace || mnemonicPressed) {
onClick();
return true;
}
break;
}
}
2007-09-19 07:57:02 +08:00
}
break;
case kMouseDownMessage:
switch (m_behaviorType) {
case kButtonWidget:
if (isEnabled()) {
setSelected(true);
m_pressedStatus = isSelected();
captureMouse();
onStartDrag();
}
return true;
case kCheckWidget:
if (isEnabled()) {
setSelected(!isSelected());
m_pressedStatus = isSelected();
captureMouse();
onStartDrag();
}
return true;
case kRadioWidget:
if (isEnabled()) {
if (!isSelected()) {
m_handleSelect = false;
setSelected(true);
m_handleSelect = true;
m_pressedStatus = isSelected();
captureMouse();
onStartDrag();
}
}
return true;
2007-09-19 07:57:02 +08:00
}
break;
case kMouseUpMessage:
if (hasCapture()) {
releaseMouse();
if (hasMouse()) {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
switch (m_behaviorType) {
case kButtonWidget: {
if (mouseMsg->right())
onRightClick();
2024-12-17 01:52:19 +08:00
else
generateButtonSelectSignal();
2024-12-17 01:52:19 +08:00
} break;
case kCheckWidget: {
// Fire onClick() event
onClick();
invalidate();
} break;
case kRadioWidget: {
setSelected(false);
setSelected(true);
// Fire onClick() event
onClick();
} break;
}
}
return true;
2007-09-19 07:57:02 +08:00
}
break;
case kMouseMoveMessage:
if (isEnabled() && hasCapture()) {
m_handleSelect = false;
onSelectWhenDragging();
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
}
void ButtonBase::generateButtonSelectSignal()
{
// Deselect
setSelected(false);
2007-09-19 07:57:02 +08:00
// Fire onClick() event
onClick();
2007-09-19 07:57:02 +08:00
}
void ButtonBase::onStartDrag()
{
// Do nothing
}
void ButtonBase::onSelectWhenDragging()
{
const bool hasMouse = this->hasMouse();
// 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);
}
}
// ======================================================================
// 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
2018-10-11 23:01:21 +08:00
CheckBox::CheckBox(const std::string& text, const 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
2018-10-11 23:01:21 +08:00
RadioButton::RadioButton(const std::string& text, const int radioGroup, const 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