mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-29 21:33:12 +00:00
Introduce "alphaslider" and "opacityslider" controls. Use "opacityslider" in cel properties (fix #1544)
They work like a regular slider but use 0%-100% or 0-255 ranges depending on configuration settings.
This commit is contained in:
parent
d331195c4c
commit
04cf5eaa15
@ -5,7 +5,7 @@
|
|||||||
<window id="cel_properties" text="@.title">
|
<window id="cel_properties" text="@.title">
|
||||||
<grid id="properties_grid" columns="4">
|
<grid id="properties_grid" columns="4">
|
||||||
<label text="@.opacity" />
|
<label text="@.opacity" />
|
||||||
<slider min="0" max="255" id="opacity" cell_align="horizontal" width="128" cell_hspan="2" />
|
<opacityslider id="opacity" cell_align="horizontal" width="128" cell_hspan="2" />
|
||||||
<button id="user_data" icon="icon_user_data" tooltip="@.user_data_tooltip" />
|
<button id="user_data" icon="icon_user_data" tooltip="@.user_data_tooltip" />
|
||||||
|
|
||||||
<label text="@.zindex" />
|
<label text="@.zindex" />
|
||||||
|
@ -336,6 +336,7 @@ if(ENABLE_UI)
|
|||||||
file_selector.cpp
|
file_selector.cpp
|
||||||
modules/gfx.cpp
|
modules/gfx.cpp
|
||||||
modules/gui.cpp
|
modules/gui.cpp
|
||||||
|
ui/alpha_slider.cpp
|
||||||
ui/app_menuitem.cpp
|
ui/app_menuitem.cpp
|
||||||
ui/backup_indicator.cpp
|
ui/backup_indicator.cpp
|
||||||
ui/browser_view.cpp
|
ui/browser_view.cpp
|
||||||
|
69
src/app/ui/alpha_slider.cpp
Normal file
69
src/app/ui/alpha_slider.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2024 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "app/ui/alpha_slider.h"
|
||||||
|
#include "ui/message.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace ui;
|
||||||
|
using namespace app::gen;
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
AlphaSlider::AlphaSlider(int value, Type type)
|
||||||
|
: ui::Slider(0, 255, value, this)
|
||||||
|
, m_type(type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlphaSlider::getSliderThemeInfo(int* min, int* max, int* value) const
|
||||||
|
{
|
||||||
|
switch (getAlphaRange()) {
|
||||||
|
case AlphaRange::PERCENTAGE:
|
||||||
|
if (min) *min = 0;
|
||||||
|
if (max) *max = 100;
|
||||||
|
if (value) *value = std::round(((double)100)*((double)getValue())/((double)getMaxValue()));
|
||||||
|
|
||||||
|
return;
|
||||||
|
case AlphaRange::EIGHT_BIT:
|
||||||
|
return Slider::getSliderThemeInfo(min, max, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlphaSlider::updateValue(int value)
|
||||||
|
{
|
||||||
|
if (getAlphaRange() == AlphaRange::PERCENTAGE)
|
||||||
|
value = std::round(((double)getMaxValue())*((double)value)/((double)100));
|
||||||
|
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AlphaSlider::onGetTextFromValue(int value)
|
||||||
|
{
|
||||||
|
char buf[128];
|
||||||
|
const char *format = "%d";
|
||||||
|
if (getAlphaRange() == AlphaRange::PERCENTAGE)
|
||||||
|
format = "%d%%";
|
||||||
|
|
||||||
|
std::snprintf(buf, sizeof(buf), format, value);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AlphaSlider::onGetValueFromText(const std::string& text)
|
||||||
|
{
|
||||||
|
std::string str = text;
|
||||||
|
if (getAlphaRange() == AlphaRange::PERCENTAGE)
|
||||||
|
str.erase(std::remove(str.begin(), str.end(), '%'), str.end());
|
||||||
|
|
||||||
|
return std::strtol(str.c_str(), nullptr, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace app
|
42
src/app/ui/alpha_slider.h
Normal file
42
src/app/ui/alpha_slider.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2024 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_UI_ALPHA_SLIDER_H_INCLUDED
|
||||||
|
#define APP_UI_ALPHA_SLIDER_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "app/pref/preferences.h"
|
||||||
|
#include "ui/slider.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
class AlphaSlider : public ui::Slider,
|
||||||
|
ui::SliderDelegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Type {ALPHA, OPACITY};
|
||||||
|
|
||||||
|
AlphaSlider(int value, Type type);
|
||||||
|
|
||||||
|
void getSliderThemeInfo(int* min, int* max, int* value) const override;
|
||||||
|
void updateValue(int value) override;
|
||||||
|
|
||||||
|
app::gen::AlphaRange getAlphaRange() const {
|
||||||
|
return (m_type == ALPHA ? Preferences::instance().range.alpha()
|
||||||
|
: Preferences::instance().range.opacity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// ui::SliderDelegate impl
|
||||||
|
std::string onGetTextFromValue(int value) override;
|
||||||
|
int onGetValueFromText(const std::string& text) override;
|
||||||
|
|
||||||
|
Type m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -17,6 +17,7 @@
|
|||||||
#include "app/i18n/strings.h"
|
#include "app/i18n/strings.h"
|
||||||
#include "app/modules/gui.h"
|
#include "app/modules/gui.h"
|
||||||
#include "app/resource_finder.h"
|
#include "app/resource_finder.h"
|
||||||
|
#include "app/ui/alpha_slider.h"
|
||||||
#include "app/ui/button_set.h"
|
#include "app/ui/button_set.h"
|
||||||
#include "app/ui/color_button.h"
|
#include "app/ui/color_button.h"
|
||||||
#include "app/ui/drop_down_button.h"
|
#include "app/ui/drop_down_button.h"
|
||||||
@ -405,6 +406,13 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
|
|||||||
widget = new Slider(min_value, max_value, min_value);
|
widget = new Slider(min_value, max_value, min_value);
|
||||||
static_cast<Slider*>(widget)->setReadOnly(readonly);
|
static_cast<Slider*>(widget)->setReadOnly(readonly);
|
||||||
}
|
}
|
||||||
|
else if (elem_name == "alphaslider" || elem_name == "opacityslider") {
|
||||||
|
const bool readonly = bool_attr(elem, "readonly", false);
|
||||||
|
widget = new AlphaSlider(0, (elem_name == "alphaslider"
|
||||||
|
? AlphaSlider::Type::ALPHA
|
||||||
|
: AlphaSlider::Type::OPACITY));
|
||||||
|
static_cast<AlphaSlider*>(widget)->setReadOnly(readonly);
|
||||||
|
}
|
||||||
else if (elem_name == "textbox") {
|
else if (elem_name == "textbox") {
|
||||||
const char* text = (elem->GetText() ? elem->GetText(): "");
|
const char* text = (elem->GetText() ? elem->GetText(): "");
|
||||||
bool wordwrap = bool_attr(elem, "wordwrap", false);
|
bool wordwrap = bool_attr(elem, "wordwrap", false);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Code Generator
|
// Aseprite Code Generator
|
||||||
// Copyright (c) 2021-2023 Igara Studio S.A.
|
// Copyright (c) 2021-2024 Igara Studio S.A.
|
||||||
// Copyright (c) 2014-2018 David Capello
|
// Copyright (c) 2014-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -157,6 +157,9 @@ static Item convert_to_item(TiXmlElement* elem)
|
|||||||
if (name == "slider")
|
if (name == "slider")
|
||||||
return item.typeIncl("ui::Slider",
|
return item.typeIncl("ui::Slider",
|
||||||
"ui/slider.h");
|
"ui/slider.h");
|
||||||
|
if (name == "alphaslider" || name == "opacityslider")
|
||||||
|
return item.typeIncl("app::AlphaSlider",
|
||||||
|
"app/ui/alpha_slider.h");
|
||||||
if (name == "splitter")
|
if (name == "splitter")
|
||||||
return item.typeIncl("ui::Splitter",
|
return item.typeIncl("ui::Splitter",
|
||||||
"ui/splitter.h");
|
"ui/splitter.h");
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite UI Library
|
// Aseprite UI Library
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2016 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
static int slider_press_x;
|
int Slider::slider_press_x;
|
||||||
static int slider_press_value;
|
int Slider::slider_press_value;
|
||||||
static bool slider_press_left;
|
bool Slider::slider_press_left;
|
||||||
|
|
||||||
Slider::Slider(int min, int max, int value, SliderDelegate* delegate)
|
Slider::Slider(int min, int max, int value, SliderDelegate* delegate)
|
||||||
: Widget(kSliderWidget)
|
: Widget(kSliderWidget)
|
||||||
@ -68,6 +68,11 @@ void Slider::getSliderThemeInfo(int* min, int* max, int* value) const
|
|||||||
if (value) *value = m_value;
|
if (value) *value = m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Slider::updateValue(int value)
|
||||||
|
{
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
std::string Slider::convertValueToText(int value) const
|
std::string Slider::convertValueToText(int value) const
|
||||||
{
|
{
|
||||||
if (m_delegate)
|
if (m_delegate)
|
||||||
@ -106,9 +111,11 @@ bool Slider::onProcessMessage(Message* msg)
|
|||||||
captureMouse();
|
captureMouse();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
int value;
|
||||||
|
getSliderThemeInfo(nullptr, nullptr, &value);
|
||||||
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
|
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
|
||||||
slider_press_x = mousePos.x;
|
slider_press_x = mousePos.x;
|
||||||
slider_press_value = m_value;
|
slider_press_value = value;
|
||||||
slider_press_left = static_cast<MouseMessage*>(msg)->left();
|
slider_press_left = static_cast<MouseMessage*>(msg)->left();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,27 +125,33 @@ bool Slider::onProcessMessage(Message* msg)
|
|||||||
|
|
||||||
case kMouseMoveMessage:
|
case kMouseMoveMessage:
|
||||||
if (hasCapture()) {
|
if (hasCapture()) {
|
||||||
int value, accuracy, range;
|
int min, max, value, range;
|
||||||
gfx::Rect rc = childrenBounds();
|
gfx::Rect rc = childrenBounds();
|
||||||
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->positionForDisplay(display());
|
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->positionForDisplay(display());
|
||||||
|
|
||||||
range = m_max - m_min + 1;
|
getSliderThemeInfo(&min, &max, &value);
|
||||||
|
|
||||||
|
range = max - min + 1;
|
||||||
|
|
||||||
// With left click
|
// With left click
|
||||||
if (slider_press_left) {
|
if (slider_press_left) {
|
||||||
value = m_min + range * (mousePos.x - rc.x) / rc.w;
|
value = min + range * (mousePos.x - rc.x) / rc.w;
|
||||||
}
|
}
|
||||||
// With right click
|
// With right click
|
||||||
else {
|
else {
|
||||||
accuracy = std::clamp(rc.w / range, 1, rc.w);
|
int w = rc.w;
|
||||||
|
if (rc.w == 0 || range > rc.w) {
|
||||||
|
w = 1;
|
||||||
|
range = 1;
|
||||||
|
}
|
||||||
|
|
||||||
value = slider_press_value +
|
value = slider_press_value +
|
||||||
(mousePos.x - slider_press_x) / accuracy;
|
(mousePos.x - slider_press_x) * range / w;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = std::clamp(value, m_min, m_max);
|
value = std::clamp(value, min, max);
|
||||||
if (m_value != value) {
|
if (getValue() != value) {
|
||||||
setValue(value);
|
updateValue(value);
|
||||||
onChange();
|
onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,22 +178,24 @@ bool Slider::onProcessMessage(Message* msg)
|
|||||||
|
|
||||||
case kKeyDownMessage:
|
case kKeyDownMessage:
|
||||||
if (hasFocus() && !isReadOnly()) {
|
if (hasFocus() && !isReadOnly()) {
|
||||||
int value = m_value;
|
int min, max, value, oldValue;
|
||||||
|
getSliderThemeInfo(&min, &max, &value);
|
||||||
|
oldValue = value;
|
||||||
|
|
||||||
switch (static_cast<KeyMessage*>(msg)->scancode()) {
|
switch (static_cast<KeyMessage*>(msg)->scancode()) {
|
||||||
case kKeyLeft: --value; break;
|
case kKeyLeft: --value; break;
|
||||||
case kKeyRight: ++value; break;
|
case kKeyRight: ++value; break;
|
||||||
case kKeyPageDown: value -= (m_max-m_min+1)/4; break;
|
case kKeyPageDown: value -= (max-min+1)/4; break;
|
||||||
case kKeyPageUp: value += (m_max-m_min+1)/4; break;
|
case kKeyPageUp: value += (max-min+1)/4; break;
|
||||||
case kKeyHome: value = m_min; break;
|
case kKeyHome: value = min; break;
|
||||||
case kKeyEnd: value = m_max; break;
|
case kKeyEnd: value = max; break;
|
||||||
default:
|
default:
|
||||||
goto not_used;
|
goto not_used;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = std::clamp(value, m_min, m_max);
|
value = std::clamp(value, min, max);
|
||||||
if (m_value != value) {
|
if (oldValue != value) {
|
||||||
setValue(value);
|
updateValue(value);
|
||||||
onChange();
|
onChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +212,7 @@ bool Slider::onProcessMessage(Message* msg)
|
|||||||
value = std::clamp(value, m_min, m_max);
|
value = std::clamp(value, m_min, m_max);
|
||||||
|
|
||||||
if (m_value != value) {
|
if (m_value != value) {
|
||||||
this->setValue(value);
|
setValue(value);
|
||||||
onChange();
|
onChange();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite UI Library
|
// Aseprite UI Library
|
||||||
// Copyright (C) 2020 Igara Studio S.A.
|
// Copyright (C) 2020-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2016 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -35,7 +35,8 @@ namespace ui {
|
|||||||
bool isReadOnly() const { return m_readOnly; }
|
bool isReadOnly() const { return m_readOnly; }
|
||||||
void setReadOnly(bool readOnly) { m_readOnly = readOnly; }
|
void setReadOnly(bool readOnly) { m_readOnly = readOnly; }
|
||||||
|
|
||||||
void getSliderThemeInfo(int* min, int* max, int* value) const;
|
virtual void getSliderThemeInfo(int* min, int* max, int* value) const;
|
||||||
|
virtual void updateValue(int value);
|
||||||
|
|
||||||
std::string convertValueToText(int value) const;
|
std::string convertValueToText(int value) const;
|
||||||
int convertTextToValue(const std::string& text) const;
|
int convertTextToValue(const std::string& text) const;
|
||||||
@ -45,6 +46,10 @@ namespace ui {
|
|||||||
obs::signal<void()> SliderReleased;
|
obs::signal<void()> SliderReleased;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
static int slider_press_x;
|
||||||
|
static int slider_press_value;
|
||||||
|
static bool slider_press_left;
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
bool onProcessMessage(Message* msg) override;
|
bool onProcessMessage(Message* msg) override;
|
||||||
void onPaint(PaintEvent& ev) override;
|
void onPaint(PaintEvent& ev) override;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user