Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ PRIVATE
fonts/fonts.qrc
qt_conf/win.qrc

ayu/ayu_ui_settings.cpp
ayu/ayu_ui_settings.h

ui/accessible/ui_accessible_factory.cpp
ui/accessible/ui_accessible_factory.h
ui/accessible/ui_accessible_item.cpp
Expand Down
58 changes: 58 additions & 0 deletions ayu/ayu_ui_settings.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2026
#include "ayu_ui_settings.h"

#include <utility>

namespace AyuUiSettings {

QString monoFont;
double wideMultiplier = 1.0;
bool materialSwitches;
int avatarCorners = kMaxAvatarCorners;

void setMonoFont(QString newFont) {
monoFont = std::move(newFont);
}

QString getMonoFont() {
return monoFont;
}

void setWideMultiplier(double val) {
wideMultiplier = val;
}

bool isWideMultiplied() {
return abs(wideMultiplier - 1.0) > 0.01;
}

int getWideMultiplied(int width, double mult) {
if (!isWideMultiplied()) {
return width;
}
const auto res = width * (wideMultiplier * mult);
return std::max(width, static_cast<int>(std::round(res)));
}

void setMaterialSwitches(bool val) {
materialSwitches = val;
}

bool isMaterialSwitches() {
return materialSwitches;
}

void setAvatarCorners(int val) {
avatarCorners = val;
}

int getAvatarCorners() {
return avatarCorners;
}

}
27 changes: 27 additions & 0 deletions ayu/ayu_ui_settings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This is the source code of AyuGram for Desktop.
//
// We do not and cannot prevent the use of our code,
// but be respectful and credit the original author.
//
// Copyright @Radolyn, 2026
#pragma once

namespace AyuUiSettings {

inline constexpr int kMaxAvatarCorners = 23;

void setMonoFont(QString newFont);
QString getMonoFont();

void setWideMultiplier(double val);

bool isWideMultiplied();
int getWideMultiplied(int width, double mult);

void setMaterialSwitches(bool val);
bool isMaterialSwitches();

void setAvatarCorners(int val);
int getAvatarCorners();

}
4 changes: 2 additions & 2 deletions ui/basic.style
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ noContactsHeight: 100px;
noContactsFont: font(fsize);
noContactsColor: windowSubTextFg;

activeFadeInDuration: 500;
activeFadeOutDuration: 3000;
activeFadeInDuration: 400;
activeFadeOutDuration: 2000;

smallCloseIcon: icon {{ "simple_close", smallCloseIconFg }};
smallCloseIconOver: icon {{ "simple_close", smallCloseIconFgOver }};
Expand Down
6 changes: 3 additions & 3 deletions ui/colors.palette
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ introBg: windowBg; // login background
introTitleFg: windowBoldFg; // login title text
introDescriptionFg: windowSubTextFg; // login description text

introCoverTopBg: #0f89d0; // intro gradient top (from)
introCoverBottomBg: #39b0f0; // intro gradient bottom (to)
introCoverIconsFg: #5ec6ff; // intro cloud graphics
introCoverTopBg: #2B2242; // intro gradient top (from)
introCoverBottomBg: #2B2242; // intro gradient bottom (to)
introCoverIconsFg: #FFFFFF; // intro cloud graphics
introCoverPlaneTrace: #5ec6ff69; // intro plane traces
introCoverPlaneInner: #c6d8e8; // intro plane part
introCoverPlaneOuter: #a1bed4; // intro plane part
Expand Down
14 changes: 11 additions & 3 deletions ui/effects/panel_animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,17 +307,25 @@ void PanelAnimation::setAlphaDuration() {
void PanelAnimation::start() {
Assert(!_finalImage.isNull());
RoundShadowAnimation::start(_finalWidth, _finalHeight, _finalImage.devicePixelRatio());
auto checkCorner = [this](const Corner &corner) {
auto maxCornerWidth = 0;
auto maxCornerHeight = 0;
auto checkCorner = [&](const Corner &corner) {
if (!corner.valid()) return;
if (_startWidth >= 0) Assert(corner.width <= _startWidth);
if (_startHeight >= 0) Assert(corner.height <= _startHeight);
maxCornerWidth = std::max(maxCornerWidth, corner.width);
maxCornerHeight = std::max(maxCornerHeight, corner.height);
Assert(corner.width <= _finalInnerWidth);
Assert(corner.height <= _finalInnerHeight);
};
checkCorner(_topLeft);
checkCorner(_topRight);
checkCorner(_bottomLeft);
checkCorner(_bottomRight);
if (_startWidth >= 0 && maxCornerWidth > _startWidth) {
_startWidth = -1;
}
if (_startHeight >= 0 && maxCornerHeight > _startHeight) {
_startHeight = -1;
}
}

auto PanelAnimation::computeState(float64 dt, float64 opacity) const
Expand Down
17 changes: 16 additions & 1 deletion ui/image/image_prepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "base/bytes.h"
#include "styles/palette.h"
#include "styles/style_basic.h"
#include "ayu/ayu_ui_settings.h"

#include <zlib.h>
#include <QtCore/QFile>
Expand Down Expand Up @@ -530,7 +531,8 @@ ReadResult Read(ReadArgs &&args) {
? Option::RoundLarge
: (radius == ImageRoundRadius::Small)
? Option::RoundSmall
: (radius == ImageRoundRadius::Ellipse)
: (radius == ImageRoundRadius::Ellipse
|| radius == ImageRoundRadius::AyuUserpic)
? Option::RoundCircle
: Option::None);
}
Expand Down Expand Up @@ -1132,6 +1134,19 @@ QImage Round(
QRect target) {
if (!static_cast<int>(corners)) {
return std::move(image);
} else if (radius == ImageRoundRadius::AyuUserpic) {
const auto corners_val = AyuUiSettings::getAvatarCorners();
if (corners_val >= AyuUiSettings::kMaxAvatarCorners) {
return Circle(std::move(image), target);
} else if (corners_val <= 0) {
return std::move(image);
}
const auto size = target.isEmpty()
? std::min(image.width(), image.height())
: std::min(target.width(), target.height());
const auto r = int(double(corners_val) / AyuUiSettings::kMaxAvatarCorners * size / 2.0)
/ style::DevicePixelRatio();
return Round(std::move(image), CornersMask(r), corners, target);
} else if (radius == ImageRoundRadius::Ellipse) {
Assert((corners & RectPart::AllCorners) == RectPart::AllCorners);
return Circle(std::move(image), target);
Expand Down
1 change: 1 addition & 0 deletions ui/image/image_prepare.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum class ImageRoundRadius {
Large,
Small,
Ellipse,
AyuUserpic,
};

namespace Images {
Expand Down
2 changes: 1 addition & 1 deletion ui/layers/layers.style
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Box {
}

boxDuration: 200;
boxRadius: 8px;
boxRadius: 6px;

boxButtonFont: font(boxFontSize semibold);
defaultBoxButtonTextStyle: TextStyle(semiboldTextStyle) {
Expand Down
7 changes: 7 additions & 0 deletions ui/style/style_core_font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
#include <glib.h>
#endif // __has_include(<glib.h>)


// AyuGram includes
#include "ayu/ayu_ui_settings.h"


void style_InitFontsResource() {
#ifdef Q_OS_MAC // Use resources from the .app bundle on macOS.

Expand Down Expand Up @@ -122,7 +127,9 @@ bool LoadCustomFont(const QString &filePath) {
}

[[nodiscard]] QString ManualMonospaceFont() {
const auto monoFont = AyuUiSettings::getMonoFont().isEmpty() ? "Cascadia Mono"_q : AyuUiSettings::getMonoFont();
const auto kTryFirst = std::initializer_list<QString>{
monoFont,
u"Cascadia Mono"_q,
u"Consolas"_q,
u"Liberation Mono"_q,
Expand Down
1 change: 1 addition & 0 deletions ui/text/text.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ struct QuotePaintCache {
std::array<QColor, kMaxQuoteOutlines> outlines;
QColor header;
QColor bg;
QColor bg2;
QColor icon;
};

Expand Down
7 changes: 7 additions & 0 deletions ui/text/text_entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ class EntityInText {
[[nodiscard]] QString data() const {
return _data;
}
void setLocal() {
_local = true;
}
[[nodiscard]] bool isLocal() const {
return _local;
}

void extendToLeft(int extent) {
_offset -= extent;
Expand Down Expand Up @@ -165,6 +171,7 @@ class EntityInText {

private:
EntityType _type = EntityType::Invalid;
bool _local = false;
int _offset = 0;
int _length = 0;
QString _data;
Expand Down
4 changes: 4 additions & 0 deletions ui/text/text_utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ TextWithEntities StrikeOut(const QString &text) {
return WithSingleEntity(text, EntityType::StrikeOut);
}

TextWithEntities Code(const QString &text) {
return WithSingleEntity(text, EntityType::Code);
}

TextWithEntities Link(const QString &text, const QString &url) {
return WithSingleEntity(text, EntityType::CustomUrl, url);
}
Expand Down
1 change: 1 addition & 0 deletions ui/text/text_utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class CustomEmoji;
[[nodiscard]] TextWithEntities Italic(const QString &text);
[[nodiscard]] TextWithEntities Underline(const QString &text);
[[nodiscard]] TextWithEntities StrikeOut(const QString &text);
[[nodiscard]] TextWithEntities Code(const QString &text);
[[nodiscard]] TextWithEntities Link(
const QString &text,
const QString &url = u"internal:action"_q);
Expand Down
54 changes: 41 additions & 13 deletions ui/widgets/checkbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,28 @@
#include <QtGui/QtEvents>
#include <QtCore/QtMath>

#include "ayu/ayu_ui_settings.h"

namespace Ui {
namespace {

int SwitchShift(not_null<const style::Toggle *> st) {
return AyuUiSettings::isMaterialSwitches() ? st->shift : st::defaultToggleShift;
}

int SwitchDiameter(not_null<const style::Toggle *> st) {
return AyuUiSettings::isMaterialSwitches() ? st->diameter : st::defaultToggleDiameter;
}

int SwitchDiameter(not_null<const style::Check *> st) {
return AyuUiSettings::isMaterialSwitches() ? st->diameter : st::defaultToggleDiameter;
}

int SwitchDiameter(not_null<const style::Radio *> st) {
return AyuUiSettings::isMaterialSwitches() ? st->diameter : st::defaultToggleDiameter;
}

} // namespace

AbstractCheckView::AbstractCheckView(int duration, bool checked, Fn<void()> updateCallback)
: _duration(duration)
Expand All @@ -37,7 +58,8 @@ void AbstractCheckView::setChecked(bool checked, anim::type animated) {
[=] { if (_updateCallback) _updateCallback(); },
_checked ? 0. : 1.,
_checked ? 1. : 0.,
_duration);
AyuUiSettings::isMaterialSwitches() ? _duration : st::defaultToggleDuration,
AyuUiSettings::isMaterialSwitches() ? anim::easeOutCubic : anim::linear);
}
checkedChangedHook(animated);
if (changed) {
Expand Down Expand Up @@ -76,7 +98,7 @@ ToggleView::ToggleView(
}

QSize ToggleView::getSize() const {
return QSize(2 * _st->border + _st->diameter + _st->width, 2 * _st->border + _st->diameter);
return QSize(2 * _st->border + SwitchDiameter(_st) + _st->width, 2 * _st->border + SwitchDiameter(_st));
}

void ToggleView::setStyle(const style::Toggle &st) {
Expand All @@ -89,14 +111,20 @@ void ToggleView::paint(QPainter &p, int left, int top, int outerWidth) {

PainterHighQualityEnabler hq(p);
auto toggled = currentAnimationValue();
auto fullWidth = _st->diameter + _st->width;
auto innerDiameter = _st->diameter - 2 * _st->shift;
auto fullWidth = SwitchDiameter(_st) + _st->width;
auto innerDiameter = SwitchDiameter(_st) - 2 * SwitchShift(_st);
auto innerRadius = float64(innerDiameter) / 2.;
auto toggleLeft = left + anim::interpolate(0, fullWidth - _st->diameter, toggled);
auto bgRect = style::rtlrect(left + _st->shift, top + _st->shift, fullWidth - 2 * _st->shift, innerDiameter, outerWidth);
auto fgRect = style::rtlrect(toggleLeft, top, _st->diameter, _st->diameter, outerWidth);
auto toggleLeft = left + anim::interpolate(0, fullWidth - SwitchDiameter(_st), toggled);
auto bgRect = style::rtlrect(left + SwitchShift(_st), top + SwitchShift(_st), fullWidth - 2 * SwitchShift(_st), innerDiameter, outerWidth);
auto fgRect = style::rtlrect(toggleLeft, top, SwitchDiameter(_st), SwitchDiameter(_st), outerWidth);
auto fgBrush = anim::brush(_st->untoggledFg, _st->toggledFg, toggled);

auto fgRectF = QRectF(fgRect);
if (AyuUiSettings::isMaterialSwitches()) {
const auto ayuToggleAnim = anim::interpolateToF(_st->animPadding, 0, toggled);
fgRectF.setRect(fgRectF.x() + ayuToggleAnim / 2., fgRectF.y() + ayuToggleAnim / 2., fgRectF.width() - ayuToggleAnim, fgRectF.height() - ayuToggleAnim);
}

p.setPen(Qt::NoPen);
p.setBrush(fgBrush);
p.drawRoundedRect(bgRect, innerRadius, innerRadius);
Expand All @@ -105,7 +133,7 @@ void ToggleView::paint(QPainter &p, int left, int top, int outerWidth) {
pen.setWidth(_st->border);
p.setPen(pen);
p.setBrush(anim::brush(_st->untoggledBg, _st->toggledBg, toggled));
p.drawEllipse(fgRect);
p.drawEllipse(fgRectF);

if (_locked || _st->xsize > 0) {
p.setPen(Qt::NoPen);
Expand All @@ -127,8 +155,8 @@ void ToggleView::paintXV(QPainter &p, int left, int top, int outerWidth, float64
if (toggled < 1) {
// Just X or X->V.
const auto xSize = 0. + _st->xsize;
const auto xLeft = left + (_st->diameter - xSize) / 2.;
const auto xTop = top + (_st->diameter - xSize) / 2.;
const auto xLeft = left + (SwitchDiameter(_st) - xSize) / 2.;
const auto xTop = top + (SwitchDiameter(_st) - xSize) / 2.;
QPointF pathX[] = {
{ xLeft, xTop + stroke },
{ xLeft + stroke, xTop },
Expand All @@ -150,7 +178,7 @@ void ToggleView::paintXV(QPainter &p, int left, int top, int outerWidth, float64
// X->V.
const auto vSize = 0. + _st->vsize;
const auto fSize = (xSize + vSize - 2. * stroke);
const auto vLeft = left + (_st->diameter - fSize) / 2.;
const auto vLeft = left + (SwitchDiameter(_st) - fSize) / 2.;
const auto vTop = 0. + xTop + _st->vshift;
QPointF pathV[] = {
{ vLeft, vTop + xSize - vSize + stroke },
Expand All @@ -177,10 +205,10 @@ void ToggleView::paintXV(QPainter &p, int left, int top, int outerWidth, float64
} else {
// Just V.
const auto xSize = 0. + _st->xsize;
const auto xTop = top + (_st->diameter - xSize) / 2.;
const auto xTop = top + (SwitchDiameter(_st) - xSize) / 2.;
const auto vSize = 0. + _st->vsize;
const auto fSize = (xSize + vSize - 2. * stroke);
const auto vLeft = left + (_st->diameter - (_st->xsize + _st->vsize - 2. * stroke)) / 2.;
const auto vLeft = left + (SwitchDiameter(_st) - (_st->xsize + _st->vsize - 2. * stroke)) / 2.;
const auto vTop = 0. + xTop + _st->vshift;
QPointF pathV[] = {
{ vLeft, vTop + xSize - vSize + stroke },
Expand Down
Loading