mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-28 18:32:50 +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">
|
||||
<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" />
|
||||
|
@ -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
|
||||
|
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
|
||||
// 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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user