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:
Martín Capello 2024-01-08 15:57:29 -03:00
parent d331195c4c
commit 04cf5eaa15
8 changed files with 170 additions and 27 deletions

View File

@ -5,7 +5,7 @@
<window id="cel_properties" text="@.title">
<grid id="properties_grid" columns="4">
<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" />
<label text="@.zindex" />

View File

@ -336,6 +336,7 @@ if(ENABLE_UI)
file_selector.cpp
modules/gfx.cpp
modules/gui.cpp
ui/alpha_slider.cpp
ui/app_menuitem.cpp
ui/backup_indicator.cpp
ui/browser_view.cpp

View 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
View 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

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -17,6 +17,7 @@
#include "app/i18n/strings.h"
#include "app/modules/gui.h"
#include "app/resource_finder.h"
#include "app/ui/alpha_slider.h"
#include "app/ui/button_set.h"
#include "app/ui/color_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);
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") {
const char* text = (elem->GetText() ? elem->GetText(): "");
bool wordwrap = bool_attr(elem, "wordwrap", false);

View File

@ -1,5 +1,5 @@
// 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
//
// 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")
return item.typeIncl("ui::Slider",
"ui/slider.h");
if (name == "alphaslider" || name == "opacityslider")
return item.typeIncl("app::AlphaSlider",
"app/ui/alpha_slider.h");
if (name == "splitter")
return item.typeIncl("ui::Splitter",
"ui/splitter.h");

View File

@ -1,5 +1,5 @@
// 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
//
// This file is released under the terms of the MIT license.
@ -24,9 +24,9 @@
namespace ui {
static int slider_press_x;
static int slider_press_value;
static bool slider_press_left;
int Slider::slider_press_x;
int Slider::slider_press_value;
bool Slider::slider_press_left;
Slider::Slider(int min, int max, int value, SliderDelegate* delegate)
: Widget(kSliderWidget)
@ -68,6 +68,11 @@ void Slider::getSliderThemeInfo(int* min, int* max, int* value) const
if (value) *value = m_value;
}
void Slider::updateValue(int value)
{
setValue(value);
}
std::string Slider::convertValueToText(int value) const
{
if (m_delegate)
@ -106,9 +111,11 @@ bool Slider::onProcessMessage(Message* msg)
captureMouse();
{
int value;
getSliderThemeInfo(nullptr, nullptr, &value);
gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
slider_press_x = mousePos.x;
slider_press_value = m_value;
slider_press_value = value;
slider_press_left = static_cast<MouseMessage*>(msg)->left();
}
@ -118,27 +125,33 @@ bool Slider::onProcessMessage(Message* msg)
case kMouseMoveMessage:
if (hasCapture()) {
int value, accuracy, range;
int min, max, value, range;
gfx::Rect rc = childrenBounds();
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
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
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 +
(mousePos.x - slider_press_x) / accuracy;
(mousePos.x - slider_press_x) * range / w;
}
value = std::clamp(value, m_min, m_max);
if (m_value != value) {
setValue(value);
value = std::clamp(value, min, max);
if (getValue() != value) {
updateValue(value);
onChange();
}
@ -165,22 +178,24 @@ bool Slider::onProcessMessage(Message* msg)
case kKeyDownMessage:
if (hasFocus() && !isReadOnly()) {
int value = m_value;
int min, max, value, oldValue;
getSliderThemeInfo(&min, &max, &value);
oldValue = value;
switch (static_cast<KeyMessage*>(msg)->scancode()) {
case kKeyLeft: --value; break;
case kKeyRight: ++value; break;
case kKeyPageDown: value -= (m_max-m_min+1)/4; break;
case kKeyPageUp: value += (m_max-m_min+1)/4; break;
case kKeyHome: value = m_min; break;
case kKeyEnd: value = m_max; break;
case kKeyPageDown: value -= (max-min+1)/4; break;
case kKeyPageUp: value += (max-min+1)/4; break;
case kKeyHome: value = min; break;
case kKeyEnd: value = max; break;
default:
goto not_used;
}
value = std::clamp(value, m_min, m_max);
if (m_value != value) {
setValue(value);
value = std::clamp(value, min, max);
if (oldValue != value) {
updateValue(value);
onChange();
}
@ -197,7 +212,7 @@ bool Slider::onProcessMessage(Message* msg)
value = std::clamp(value, m_min, m_max);
if (m_value != value) {
this->setValue(value);
setValue(value);
onChange();
}
return true;

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2024 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -35,7 +35,8 @@ namespace ui {
bool isReadOnly() const { return m_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;
int convertTextToValue(const std::string& text) const;
@ -45,6 +46,10 @@ namespace ui {
obs::signal<void()> SliderReleased;
protected:
static int slider_press_x;
static int slider_press_value;
static bool slider_press_left;
// Events
bool onProcessMessage(Message* msg) override;
void onPaint(PaintEvent& ev) override;