2017-02-03 23:07:26 +03:00
/*
This file is part of Telegram Desktop ,
2018-01-03 13:23:14 +03:00
the official desktop application for the Telegram messaging service .
2017-02-03 23:07:26 +03:00
2018-01-03 13:23:14 +03:00
For license and copyright information please follow this link :
https : //github.com/telegramdesktop/tdesktop/blob/master/LEGAL
2017-02-03 23:07:26 +03:00
*/
# include "window/themes/window_theme_editor_block.h"
2023-01-15 05:25:41 +03:00
# include "base/call_delayed.h"
# include "boxes/abstract_box.h"
2017-04-13 11:27:10 +03:00
# include "lang/lang_keys.h"
2023-01-15 05:25:41 +03:00
# include "ui/effects/ripple_animation.h"
# include "ui/layers/generic_box.h"
# include "ui/painter.h"
2023-01-15 05:39:15 +03:00
# include "ui/widgets/color_editor.h"
2023-01-15 05:25:41 +03:00
# include "ui/widgets/shadow.h"
# include "styles/style_layers.h"
# include "styles/style_window.h"
2017-02-03 23:07:26 +03:00
namespace Window {
namespace Theme {
namespace {
2022-11-30 00:46:36 +03:00
auto SearchSplitter = QRegularExpression ( u " [ \\ @ \\ s \\ - \\ + \\ ( \\ ) \\ [ \\ ] \\ { \\ } \\ < \\ > \\ , \\ . \\ : \\ ! \\ _ \\ ; \\ \" \\ ' \\ x0 \\ #] " _q ) ;
2017-02-03 23:07:26 +03:00
} // namespace
class EditorBlock : : Row {
public :
Row ( const QString & name , const QString & copyOf , QColor value ) ;
QString name ( ) const {
return _name ;
}
void setCopyOf ( const QString & copyOf ) {
_copyOf = copyOf ;
fillSearchIndex ( ) ;
}
QString copyOf ( ) const {
return _copyOf ;
}
void setValue ( QColor value ) ;
const QColor & value ( ) const {
return _value ;
}
QString description ( ) const {
2019-04-08 15:53:08 +04:00
return _description . toString ( ) ;
2017-02-03 23:07:26 +03:00
}
2019-06-12 15:26:04 +02:00
const Ui : : Text : : String & descriptionText ( ) const {
2017-02-03 23:07:26 +03:00
return _description ;
}
void setDescription ( const QString & description ) {
_description . setText ( st : : defaultTextStyle , description ) ;
fillSearchIndex ( ) ;
}
2021-09-03 14:09:57 +03:00
const base : : flat_set < QString > & searchWords ( ) const {
2017-02-03 23:07:26 +03:00
return _searchWords ;
}
bool searchWordsContain ( const QString & needle ) const {
2021-09-03 14:09:57 +03:00
for ( const auto & word : _searchWords ) {
2017-02-03 23:07:26 +03:00
if ( word . startsWith ( needle ) ) {
return true ;
}
}
return false ;
}
2021-09-03 14:09:57 +03:00
const base : : flat_set < QChar > & searchStartChars ( ) const {
2017-02-03 23:07:26 +03:00
return _searchStartChars ;
}
void setTop ( int top ) {
_top = top ;
}
int top ( ) const {
return _top ;
}
void setHeight ( int height ) {
_height = height ;
}
int height ( ) const {
return _height ;
}
Ui : : RippleAnimation * ripple ( ) const {
return _ripple . get ( ) ;
}
2017-02-21 16:45:56 +03:00
Ui : : RippleAnimation * setRipple ( std : : unique_ptr < Ui : : RippleAnimation > ripple ) const {
_ripple = std : : move ( ripple ) ;
2017-02-03 23:07:26 +03:00
return _ripple . get ( ) ;
}
void resetRipple ( ) const {
_ripple = nullptr ;
}
private :
void fillValueString ( ) ;
void fillSearchIndex ( ) ;
QString _name ;
QString _copyOf ;
QColor _value ;
QString _valueString ;
2019-06-12 15:26:04 +02:00
Ui : : Text : : String _description = { st : : windowMinWidth / 2 } ;
2017-02-03 23:07:26 +03:00
2021-09-03 14:09:57 +03:00
base : : flat_set < QString > _searchWords ;
base : : flat_set < QChar > _searchStartChars ;
2017-02-03 23:07:26 +03:00
int _top = 0 ;
int _height = 0 ;
2017-02-21 16:45:56 +03:00
mutable std : : unique_ptr < Ui : : RippleAnimation > _ripple ;
2017-02-03 23:07:26 +03:00
} ;
EditorBlock : : Row : : Row ( const QString & name , const QString & copyOf , QColor value )
: _name ( name )
, _copyOf ( copyOf ) {
setValue ( value ) ;
}
void EditorBlock : : Row : : setValue ( QColor value ) {
_value = value ;
fillValueString ( ) ;
fillSearchIndex ( ) ;
}
void EditorBlock : : Row : : fillValueString ( ) {
2018-04-07 12:47:08 +04:00
auto addHex = [ = ] ( int code ) {
2017-02-03 23:07:26 +03:00
if ( code > = 0 & & code < 10 ) {
2024-10-10 21:15:57 +03:00
_valueString . append ( QChar ( ' 0 ' + code ) ) ;
2017-02-03 23:07:26 +03:00
} else if ( code > = 10 & & code < 16 ) {
2024-10-10 21:15:57 +03:00
_valueString . append ( QChar ( ' a ' + ( code - 10 ) ) ) ;
2017-02-03 23:07:26 +03:00
}
} ;
2018-04-07 12:47:08 +04:00
auto addCode = [ = ] ( int code ) {
2017-02-03 23:07:26 +03:00
addHex ( code / 16 ) ;
addHex ( code % 16 ) ;
} ;
_valueString . resize ( 0 ) ;
_valueString . reserve ( 9 ) ;
_valueString . append ( ' # ' ) ;
addCode ( _value . red ( ) ) ;
addCode ( _value . green ( ) ) ;
addCode ( _value . blue ( ) ) ;
if ( _value . alpha ( ) ! = 255 ) {
addCode ( _value . alpha ( ) ) ;
}
}
void EditorBlock : : Row : : fillSearchIndex ( ) {
_searchWords . clear ( ) ;
_searchStartChars . clear ( ) ;
2021-09-03 14:09:57 +03:00
const auto toIndex = _name
+ ' ' + _copyOf
+ ' ' + TextUtilities : : RemoveAccents ( _description . toString ( ) )
+ ' ' + _valueString ;
const auto words = toIndex . toLower ( ) . split (
SearchSplitter ,
2021-10-19 17:00:21 +04:00
Qt : : SkipEmptyParts ) ;
2021-09-03 14:09:57 +03:00
for ( const auto & word : words ) {
_searchWords . emplace ( word ) ;
_searchStartChars . emplace ( word [ 0 ] ) ;
2017-02-03 23:07:26 +03:00
}
}
2022-11-11 11:21:40 +04:00
EditorBlock : : EditorBlock ( QWidget * parent , Type type , Context * context )
: RpWidget ( parent )
2017-02-03 23:07:26 +03:00
, _type ( type )
, _context ( context )
2021-06-02 20:36:24 +04:00
, _transparent ( style : : TransparentPlaceholder ( ) ) {
2017-02-03 23:07:26 +03:00
setMouseTracking ( true ) ;
2022-11-11 11:21:40 +04:00
_context - > updated . events (
) | rpl : : start_with_next ( [ = ] {
2017-02-03 23:07:26 +03:00
if ( _mouseSelection ) {
_lastGlobalPos = QCursor : : pos ( ) ;
updateSelected ( mapFromGlobal ( _lastGlobalPos ) ) ;
}
update ( ) ;
2022-11-11 11:21:40 +04:00
} , lifetime ( ) ) ;
2017-02-03 23:07:26 +03:00
if ( _type = = Type : : Existing ) {
2022-11-11 11:21:40 +04:00
_context - > appended . events (
) | rpl : : start_with_next ( [ = ] ( const Context : : AppendData & added ) {
2017-02-03 23:07:26 +03:00
auto name = added . name ;
auto value = added . value ;
feed ( name , value ) ;
feedDescription ( name , added . description ) ;
auto row = findRow ( name ) ;
2017-08-17 12:06:26 +03:00
Assert ( row ! = nullptr ) ;
2017-02-03 23:07:26 +03:00
auto possibleCopyOf = added . possibleCopyOf ;
auto copyOf = checkCopyOf ( findRowIndex ( row ) , possibleCopyOf ) ? possibleCopyOf : QString ( ) ;
removeFromSearch ( * row ) ;
row - > setCopyOf ( copyOf ) ;
addToSearch ( * row ) ;
2022-11-11 11:21:40 +04:00
_context - > changed . fire ( { QStringList ( name ) , value } ) ;
_context - > resized . fire ( { } ) ;
_context - > pending . fire ( { name , copyOf , value } ) ;
} , lifetime ( ) ) ;
2017-02-03 23:07:26 +03:00
} else {
2022-11-11 11:21:40 +04:00
_context - > changed . events (
) | rpl : : start_with_next ( [ = ] ( const Context : : ChangeData & data ) {
2017-02-03 23:07:26 +03:00
checkCopiesChanged ( 0 , data . names , data . value ) ;
2022-11-11 11:21:40 +04:00
} , lifetime ( ) ) ;
2017-02-03 23:07:26 +03:00
}
}
void EditorBlock : : feed ( const QString & name , QColor value , const QString & copyOfExisting ) {
if ( findRow ( name ) ) {
// Remove the existing row and mark all its copies as unique keys.
LOG ( ( " Theme Warning: Color value '%1' appears more than once in the color scheme. " ) . arg ( name ) ) ;
removeRow ( name ) ;
}
addRow ( name , copyOfExisting , value ) ;
}
bool EditorBlock : : feedCopy ( const QString & name , const QString & copyOf ) {
if ( auto row = findRow ( copyOf ) ) {
2023-09-06 14:10:23 +03:00
if ( copyOf = = name ) {
LOG ( ( " Theme Warning: Skipping value '%1: %2' (the value refers to itself.) " ) . arg ( name , copyOf ) ) ;
return true ;
}
2017-02-03 23:07:26 +03:00
if ( findRow ( name ) ) {
// Remove the existing row and mark all its copies as unique keys.
LOG ( ( " Theme Warning: Color value '%1' appears more than once in the color scheme. " ) . arg ( name ) ) ;
removeRow ( name ) ;
// row was invalidated by removeRow() call.
row = findRow ( copyOf ) ;
2023-09-06 14:10:23 +03:00
// Should not happen, but still check.
if ( ! row ) {
return true ;
}
2017-02-03 23:07:26 +03:00
}
addRow ( name , copyOf , row - > value ( ) ) ;
} else {
2021-03-13 15:50:34 +04:00
LOG ( ( " Theme Warning: Skipping value '%1: %2' (expected a color value in #rrggbb or #rrggbbaa or a previously defined key in the color scheme) " ) . arg ( name , copyOf ) ) ;
2017-02-03 23:07:26 +03:00
}
return true ;
}
void EditorBlock : : removeRow ( const QString & name , bool removeCopyReferences ) {
auto it = _indices . find ( name ) ;
2017-08-17 12:06:26 +03:00
Assert ( it ! = _indices . cend ( ) ) ;
2017-02-03 23:07:26 +03:00
auto index = it . value ( ) ;
2017-02-21 16:45:56 +03:00
for ( auto i = index + 1 , count = static_cast < int > ( _data . size ( ) ) ; i ! = count ; + + i ) {
2017-02-03 23:07:26 +03:00
auto & row = _data [ i ] ;
removeFromSearch ( row ) ;
_indices [ row . name ( ) ] = i - 1 ;
if ( removeCopyReferences & & row . copyOf ( ) = = name ) {
row . setCopyOf ( QString ( ) ) ;
}
}
2020-05-01 16:27:55 +04:00
removeFromSearch ( _data [ index ] ) ;
2017-02-03 23:07:26 +03:00
_data . erase ( _data . begin ( ) + index ) ;
_indices . erase ( it ) ;
2017-02-21 16:45:56 +03:00
for ( auto i = index , count = static_cast < int > ( _data . size ( ) ) ; i ! = count ; + + i ) {
2017-02-03 23:07:26 +03:00
addToSearch ( _data [ i ] ) ;
}
}
void EditorBlock : : addToSearch ( const Row & row ) {
auto query = _searchQuery ;
if ( ! query . isEmpty ( ) ) resetSearch ( ) ;
auto index = findRowIndex ( & row ) ;
2021-09-08 13:53:54 +03:00
for ( const auto & ch : row . searchStartChars ( ) ) {
2017-02-03 23:07:26 +03:00
_searchIndex [ ch ] . insert ( index ) ;
}
if ( ! query . isEmpty ( ) ) searchByQuery ( query ) ;
}
void EditorBlock : : removeFromSearch ( const Row & row ) {
auto query = _searchQuery ;
if ( ! query . isEmpty ( ) ) resetSearch ( ) ;
auto index = findRowIndex ( & row ) ;
2021-09-08 13:53:54 +03:00
for ( const auto & ch : row . searchStartChars ( ) ) {
2021-09-03 14:09:57 +03:00
const auto i = _searchIndex . find ( ch ) ;
if ( i ! = end ( _searchIndex ) ) {
i - > second . remove ( index ) ;
if ( i - > second . empty ( ) ) {
_searchIndex . erase ( i ) ;
2017-02-03 23:07:26 +03:00
}
}
}
if ( ! query . isEmpty ( ) ) searchByQuery ( query ) ;
}
void EditorBlock : : filterRows ( const QString & query ) {
searchByQuery ( query ) ;
}
void EditorBlock : : chooseRow ( ) {
if ( _selected < 0 ) {
return ;
}
activateRow ( rowAtIndex ( _selected ) ) ;
}
void EditorBlock : : activateRow ( const Row & row ) {
2023-01-15 05:25:41 +03:00
if ( _context - > colorEditor . editor ) {
2017-02-03 23:07:26 +03:00
if ( _type = = Type : : Existing ) {
_context - > possibleCopyOf = row . name ( ) ;
2023-01-15 05:25:41 +03:00
_context - > colorEditor . editor - > showColor ( row . value ( ) ) ;
2017-02-03 23:07:26 +03:00
}
} else {
_editing = findRowIndex ( & row ) ;
2023-01-15 05:25:41 +03:00
const auto name = row . name ( ) ;
const auto value = row . value ( ) ;
Ui : : show ( Box ( [ = ] ( not_null < Ui : : GenericBox * > box ) {
const auto editor = box - > addRow ( object_ptr < ColorEditor > (
box ,
ColorEditor : : Mode : : RGBA ,
value ) ) ;
struct State {
rpl : : lifetime cancelLifetime ;
} ;
const auto state = editor - > lifetime ( ) . make_state < State > ( ) ;
const auto save = crl : : guard ( this , [ = ] {
state - > cancelLifetime . destroy ( ) ;
2024-01-16 21:09:45 +04:00
saveEditing ( editor - > color ( ) ) ;
2023-01-15 05:25:41 +03:00
} ) ;
box - > boxClosing (
) | rpl : : start_with_next ( crl : : guard ( this , [ = ] {
2017-02-03 23:07:26 +03:00
cancelEditing ( ) ;
2023-01-15 05:25:41 +03:00
} ) , state - > cancelLifetime ) ;
editor - > submitRequests (
) | rpl : : start_with_next ( save , editor - > lifetime ( ) ) ;
box - > setFocusCallback ( [ = ] {
editor - > setInnerFocus ( ) ;
} ) ;
box - > addButton ( tr : : lng_settings_save ( ) , save ) ;
box - > addButton ( tr : : lng_cancel ( ) , [ = ] { box - > closeBox ( ) ; } ) ;
box - > setTitle ( rpl : : single ( name ) ) ;
box - > setWidth ( editor - > width ( ) ) ;
_context - > colorEditor . box = box ;
_context - > colorEditor . editor = editor ;
_context - > name = name ;
2022-11-11 11:21:40 +04:00
_context - > updated . fire ( { } ) ;
2023-01-15 05:25:41 +03:00
} ) ) ;
2017-02-03 23:07:26 +03:00
}
}
bool EditorBlock : : selectSkip ( int direction ) {
_mouseSelection = false ;
2018-09-15 20:03:15 +03:00
auto maxSelected = size_type ( isSearch ( )
? _searchResults . size ( )
: _data . size ( ) ) - 1 ;
2017-02-03 23:07:26 +03:00
auto newSelected = _selected + direction ;
if ( newSelected < - 1 | | newSelected > maxSelected ) {
newSelected = maxSelected ;
}
2021-07-14 02:16:03 +04:00
if ( newSelected ! = _selected ) {
2017-02-03 23:07:26 +03:00
setSelected ( newSelected ) ;
scrollToSelected ( ) ;
return ( newSelected > = 0 ) ;
}
return false ;
}
void EditorBlock : : scrollToSelected ( ) {
if ( _selected > = 0 ) {
2022-11-11 11:21:40 +04:00
const auto & row = rowAtIndex ( _selected ) ;
_context - > scroll . fire ( { _type , row . top ( ) , row . height ( ) } ) ;
2017-02-03 23:07:26 +03:00
}
}
void EditorBlock : : searchByQuery ( QString query ) {
2021-09-03 14:09:57 +03:00
const auto words = TextUtilities : : PrepareSearchWords (
query ,
& SearchSplitter ) ;
2017-07-06 14:37:42 +03:00
query = words . isEmpty ( ) ? QString ( ) : words . join ( ' ' ) ;
2017-02-03 23:07:26 +03:00
if ( _searchQuery ! = query ) {
setSelected ( - 1 ) ;
setPressed ( - 1 ) ;
_searchQuery = query ;
_searchResults . clear ( ) ;
2021-09-03 14:09:57 +03:00
auto toFilter = ( base : : flat_set < int > * ) nullptr ;
for ( const auto & word : words ) {
2017-02-03 23:07:26 +03:00
if ( word . isEmpty ( ) ) continue ;
2021-09-03 14:09:57 +03:00
const auto i = _searchIndex . find ( word [ 0 ] ) ;
if ( i = = end ( _searchIndex ) | | i - > second . empty ( ) ) {
toFilter = nullptr ;
2017-02-03 23:07:26 +03:00
break ;
2021-09-03 14:09:57 +03:00
} else if ( ! toFilter | | i - > second . size ( ) < toFilter - > size ( ) ) {
toFilter = & i - > second ;
2017-02-03 23:07:26 +03:00
}
}
2021-09-03 14:09:57 +03:00
if ( toFilter ) {
const auto allWordsFound = [ & ] ( const Row & row ) {
for ( const auto & word : words ) {
2017-02-03 23:07:26 +03:00
if ( ! row . searchWordsContain ( word ) ) {
return false ;
}
}
return true ;
} ;
2021-09-03 14:09:57 +03:00
for ( const auto index : * toFilter ) {
2017-02-03 23:07:26 +03:00
if ( allWordsFound ( _data [ index ] ) ) {
_searchResults . push_back ( index ) ;
}
}
}
2022-11-11 11:21:40 +04:00
_context - > resized . fire ( { } ) ;
2017-02-03 23:07:26 +03:00
}
}
const QColor * EditorBlock : : find ( const QString & name ) {
if ( auto row = findRow ( name ) ) {
return & row - > value ( ) ;
}
return nullptr ;
}
bool EditorBlock : : feedDescription ( const QString & name , const QString & description ) {
if ( auto row = findRow ( name ) ) {
removeFromSearch ( * row ) ;
row - > setDescription ( description ) ;
addToSearch ( * row ) ;
return true ;
}
return false ;
}
2019-08-20 19:03:20 +03:00
void EditorBlock : : sortByDistance ( const QColor & to ) {
2019-08-26 17:08:33 +03:00
auto toHue = int ( ) ;
auto toSaturation = int ( ) ;
auto toLightness = int ( ) ;
to . getHsl ( & toHue , & toSaturation , & toLightness ) ;
2019-08-20 19:03:20 +03:00
ranges : : sort ( _data , ranges : : less ( ) , [ & ] ( const Row & row ) {
2019-08-26 17:08:33 +03:00
auto fromHue = int ( ) ;
auto fromSaturation = int ( ) ;
auto fromLightness = int ( ) ;
row . value ( ) . getHsl ( & fromHue , & fromSaturation , & fromLightness ) ;
2019-08-27 16:59:15 +03:00
if ( ! row . copyOf ( ) . isEmpty ( ) ) {
2019-08-26 17:08:33 +03:00
return 365 ;
}
const auto a = std : : abs ( fromHue - toHue ) ;
const auto b = 360 + fromHue - toHue ;
const auto c = 360 + toHue - fromHue ;
2019-08-27 16:59:15 +03:00
if ( std : : min ( a , std : : min ( b , c ) ) > 15 ) {
return 363 ;
}
return 255 - fromSaturation ;
2019-08-20 19:03:20 +03:00
} ) ;
}
2017-02-03 23:07:26 +03:00
template < typename Callback >
void EditorBlock : : enumerateRows ( Callback callback ) {
if ( isSearch ( ) ) {
2021-09-03 14:09:57 +03:00
for ( const auto index : _searchResults ) {
2017-02-03 23:07:26 +03:00
if ( ! callback ( _data [ index ] ) ) {
break ;
}
}
} else {
2017-02-21 16:45:56 +03:00
for ( auto & row : _data ) {
2017-02-03 23:07:26 +03:00
if ( ! callback ( row ) ) {
break ;
}
}
}
}
template < typename Callback >
void EditorBlock : : enumerateRows ( Callback callback ) const {
if ( isSearch ( ) ) {
2021-09-03 14:09:57 +03:00
for ( const auto index : _searchResults ) {
2017-02-03 23:07:26 +03:00
if ( ! callback ( _data [ index ] ) ) {
break ;
}
}
} else {
2020-05-28 14:00:51 +04:00
for ( const auto & row : _data ) {
2017-02-03 23:07:26 +03:00
if ( ! callback ( row ) ) {
break ;
}
}
}
}
template < typename Callback >
void EditorBlock : : enumerateRowsFrom ( int top , Callback callback ) {
auto started = false ;
auto index = 0 ;
enumerateRows ( [ top , callback , & started , & index ] ( Row & row ) {
if ( ! started ) {
if ( row . top ( ) + row . height ( ) < = top ) {
+ + index ;
return true ;
}
started = true ;
}
return callback ( index + + , row ) ;
} ) ;
}
template < typename Callback >
void EditorBlock : : enumerateRowsFrom ( int top , Callback callback ) const {
auto started = false ;
enumerateRows ( [ top , callback , & started ] ( const Row & row ) {
if ( ! started ) {
if ( row . top ( ) + row . height ( ) < = top ) {
return true ;
}
started = true ;
}
return callback ( row ) ;
} ) ;
}
int EditorBlock : : resizeGetHeight ( int newWidth ) {
auto result = 0 ;
auto descriptionWidth = newWidth - st : : themeEditorMargin . left ( ) - st : : themeEditorMargin . right ( ) ;
2018-04-07 12:47:08 +04:00
enumerateRows ( [ & ] ( Row & row ) {
2017-02-03 23:07:26 +03:00
row . setTop ( result ) ;
auto height = row . height ( ) ;
if ( ! height ) {
height = st : : themeEditorMargin . top ( ) + st : : themeEditorSampleSize . height ( ) ;
if ( ! row . descriptionText ( ) . isEmpty ( ) ) {
height + = st : : themeEditorDescriptionSkip + row . descriptionText ( ) . countHeight ( descriptionWidth ) ;
}
height + = st : : themeEditorMargin . bottom ( ) ;
row . setHeight ( height ) ;
}
result + = row . height ( ) ;
return true ;
} ) ;
if ( _type = = Type : : New ) {
setHidden ( ! result ) ;
}
if ( _type = = Type : : Existing & & ! result & & ! isSearch ( ) ) {
return st : : noContactsHeight ;
}
return result ;
}
void EditorBlock : : mousePressEvent ( QMouseEvent * e ) {
updateSelected ( e - > pos ( ) ) ;
setPressed ( _selected ) ;
}
void EditorBlock : : mouseReleaseEvent ( QMouseEvent * e ) {
auto pressed = _pressed ;
setPressed ( - 1 ) ;
if ( pressed = = _selected ) {
2023-01-15 05:25:41 +03:00
if ( _context - > colorEditor . box ) {
2017-02-03 23:07:26 +03:00
chooseRow ( ) ;
} else if ( _selected > = 0 ) {
2019-09-26 13:55:35 +03:00
base : : call_delayed ( st : : defaultRippleAnimation . hideDuration , this , [ this , index = findRowIndex ( & rowAtIndex ( _selected ) ) ] {
2017-02-03 23:07:26 +03:00
if ( index > = 0 & & index < _data . size ( ) ) {
activateRow ( _data [ index ] ) ;
}
} ) ;
}
}
}
void EditorBlock : : saveEditing ( QColor value ) {
if ( _editing < 0 ) {
return ;
}
auto & row = _data [ _editing ] ;
auto name = row . name ( ) ;
if ( _type = = Type : : New ) {
setSelected ( - 1 ) ;
setPressed ( - 1 ) ;
auto possibleCopyOf = _context - > possibleCopyOf . isEmpty ( ) ? row . copyOf ( ) : _context - > possibleCopyOf ;
auto color = value ;
auto description = row . description ( ) ;
removeRow ( name , false ) ;
2022-11-11 11:21:40 +04:00
_context - > appended . fire ( { name , possibleCopyOf , color , description } ) ;
2017-02-03 23:07:26 +03:00
} else if ( _type = = Type : : Existing ) {
removeFromSearch ( row ) ;
auto valueChanged = ( row . value ( ) ! = value ) ;
if ( valueChanged ) {
row . setValue ( value ) ;
}
auto possibleCopyOf = _context - > possibleCopyOf . isEmpty ( ) ? row . copyOf ( ) : _context - > possibleCopyOf ;
auto copyOf = checkCopyOf ( _editing , possibleCopyOf ) ? possibleCopyOf : QString ( ) ;
auto copyOfChanged = ( row . copyOf ( ) ! = copyOf ) ;
if ( copyOfChanged ) {
row . setCopyOf ( copyOf ) ;
}
addToSearch ( row ) ;
if ( valueChanged | | copyOfChanged ) {
checkCopiesChanged ( _editing + 1 , QStringList ( name ) , value ) ;
2022-11-11 11:21:40 +04:00
_context - > pending . fire ( { name , copyOf , value } ) ;
2017-02-03 23:07:26 +03:00
}
}
cancelEditing ( ) ;
}
void EditorBlock : : checkCopiesChanged ( int startIndex , QStringList names , QColor value ) {
2017-02-21 16:45:56 +03:00
for ( auto i = startIndex , count = static_cast < int > ( _data . size ( ) ) ; i ! = count ; + + i ) {
2017-02-03 23:07:26 +03:00
auto & checkIfIsCopy = _data [ i ] ;
if ( names . contains ( checkIfIsCopy . copyOf ( ) ) ) {
removeFromSearch ( checkIfIsCopy ) ;
checkIfIsCopy . setValue ( value ) ;
names . push_back ( checkIfIsCopy . name ( ) ) ;
addToSearch ( checkIfIsCopy ) ;
}
}
if ( _type = = Type : : Existing ) {
2022-11-11 11:21:40 +04:00
_context - > changed . fire ( { names , value } ) ;
2017-02-03 23:07:26 +03:00
}
}
void EditorBlock : : cancelEditing ( ) {
if ( _editing > = 0 ) {
updateRow ( _data [ _editing ] ) ;
}
_editing = - 1 ;
2023-01-15 05:25:41 +03:00
if ( const auto box = base : : take ( _context - > colorEditor . box ) ) {
2017-02-03 23:07:26 +03:00
box - > closeBox ( ) ;
}
_context - > possibleCopyOf = QString ( ) ;
if ( ! _context - > name . isEmpty ( ) ) {
_context - > name = QString ( ) ;
2022-11-11 11:21:40 +04:00
_context - > updated . fire ( { } ) ;
2017-02-03 23:07:26 +03:00
}
}
bool EditorBlock : : checkCopyOf ( int index , const QString & possibleCopyOf ) {
auto copyOfIndex = findRowIndex ( possibleCopyOf ) ;
return ( copyOfIndex > = 0
& & index > copyOfIndex
& & _data [ copyOfIndex ] . value ( ) . toRgb ( ) = = _data [ index ] . value ( ) . toRgb ( ) ) ;
}
void EditorBlock : : mouseMoveEvent ( QMouseEvent * e ) {
if ( _lastGlobalPos ! = e - > globalPos ( ) | | _mouseSelection ) {
_lastGlobalPos = e - > globalPos ( ) ;
updateSelected ( e - > pos ( ) ) ;
}
}
void EditorBlock : : updateSelected ( QPoint localPosition ) {
_mouseSelection = true ;
auto top = localPosition . y ( ) ;
auto underMouseIndex = - 1 ;
enumerateRowsFrom ( top , [ & underMouseIndex , top ] ( int index , const Row & row ) {
if ( row . top ( ) < = top ) {
underMouseIndex = index ;
}
return false ;
} ) ;
setSelected ( underMouseIndex ) ;
}
2017-02-11 14:24:37 +03:00
void EditorBlock : : leaveEventHook ( QEvent * e ) {
2017-02-03 23:07:26 +03:00
_mouseSelection = false ;
setSelected ( - 1 ) ;
}
void EditorBlock : : paintEvent ( QPaintEvent * e ) {
Painter p ( this ) ;
auto clip = e - > rect ( ) ;
2017-02-21 16:45:56 +03:00
if ( _data . empty ( ) ) {
2017-02-03 23:07:26 +03:00
p . fillRect ( clip , st : : dialogsBg ) ;
p . setFont ( st : : noContactsFont ) ;
p . setPen ( st : : noContactsColor ) ;
2019-06-19 17:09:03 +02:00
p . drawText ( QRect ( 0 , 0 , width ( ) , st : : noContactsHeight ) , tr : : lng_theme_editor_no_keys ( tr : : now ) ) ;
2017-02-03 23:07:26 +03:00
}
auto cliptop = clip . y ( ) ;
auto clipbottom = cliptop + clip . height ( ) ;
2019-04-02 13:13:30 +04:00
enumerateRowsFrom ( cliptop , [ & ] ( int index , const Row & row ) {
2017-02-03 23:07:26 +03:00
if ( row . top ( ) > = clipbottom ) {
return false ;
}
2019-04-02 13:13:30 +04:00
paintRow ( p , index , row ) ;
2017-02-03 23:07:26 +03:00
return true ;
} ) ;
}
2019-04-02 13:13:30 +04:00
void EditorBlock : : paintRow ( Painter & p , int index , const Row & row ) {
2017-02-03 23:07:26 +03:00
auto rowTop = row . top ( ) + st : : themeEditorMargin . top ( ) ;
auto rect = QRect ( 0 , row . top ( ) , width ( ) , row . height ( ) ) ;
auto selected = ( _pressed > = 0 ) ? ( index = = _pressed ) : ( index = = _selected ) ;
auto active = ( findRowIndex ( & row ) = = _editing ) ;
p . fillRect ( rect , active ? st : : dialogsBgActive : selected ? st : : dialogsBgOver : st : : dialogsBg ) ;
if ( auto ripple = row . ripple ( ) ) {
2019-04-02 13:13:30 +04:00
ripple - > paint ( p , 0 , row . top ( ) , width ( ) , & ( active ? st : : activeButtonBgRipple : st : : windowBgRipple ) - > c ) ;
2017-02-03 23:07:26 +03:00
if ( ripple - > empty ( ) ) {
row . resetRipple ( ) ;
}
}
auto sample = QRect ( width ( ) - st : : themeEditorMargin . right ( ) - st : : themeEditorSampleSize . width ( ) , rowTop , st : : themeEditorSampleSize . width ( ) , st : : themeEditorSampleSize . height ( ) ) ;
Ui : : Shadow : : paint ( p , sample , width ( ) , st : : defaultRoundShadow ) ;
if ( row . value ( ) . alpha ( ) ! = 255 ) {
p . fillRect ( myrtlrect ( sample ) , _transparent ) ;
}
p . fillRect ( myrtlrect ( sample ) , row . value ( ) ) ;
auto rowWidth = width ( ) - st : : themeEditorMargin . left ( ) - st : : themeEditorMargin . right ( ) ;
auto nameWidth = rowWidth - st : : themeEditorSampleSize . width ( ) - st : : themeEditorDescriptionSkip ;
p . setFont ( st : : themeEditorNameFont ) ;
p . setPen ( active ? st : : dialogsNameFgActive : selected ? st : : dialogsNameFgOver : st : : dialogsNameFg ) ;
p . drawTextLeft ( st : : themeEditorMargin . left ( ) , rowTop , width ( ) , st : : themeEditorNameFont - > elided ( row . name ( ) , nameWidth ) ) ;
if ( ! row . copyOf ( ) . isEmpty ( ) ) {
auto copyTop = rowTop + st : : themeEditorNameFont - > height ;
p . setFont ( st : : themeEditorCopyNameFont ) ;
p . drawTextLeft ( st : : themeEditorMargin . left ( ) , copyTop , width ( ) , st : : themeEditorCopyNameFont - > elided ( " = " + row . copyOf ( ) , nameWidth ) ) ;
}
if ( ! row . descriptionText ( ) . isEmpty ( ) ) {
auto descriptionTop = rowTop + st : : themeEditorSampleSize . height ( ) + st : : themeEditorDescriptionSkip ;
p . setPen ( active ? st : : dialogsTextFgActive : selected ? st : : dialogsTextFgOver : st : : dialogsTextFg ) ;
row . descriptionText ( ) . drawLeft ( p , st : : themeEditorMargin . left ( ) , descriptionTop , rowWidth , width ( ) ) ;
}
if ( isEditing ( ) & & ! active & & ( _type = = Type : : New | | ( _editing > = 0 & & findRowIndex ( & row ) > = _editing ) ) ) {
p . fillRect ( rect , st : : layerBg ) ;
}
}
void EditorBlock : : setSelected ( int selected ) {
if ( isEditing ( ) ) {
if ( _type = = Type : : New ) {
selected = - 1 ;
} else if ( _editing > = 0 & & selected > = 0 & & findRowIndex ( & rowAtIndex ( selected ) ) > = _editing ) {
selected = - 1 ;
}
}
if ( _selected ! = selected ) {
if ( _selected > = 0 ) updateRow ( rowAtIndex ( _selected ) ) ;
_selected = selected ;
if ( _selected > = 0 ) updateRow ( rowAtIndex ( _selected ) ) ;
setCursor ( ( _selected > = 0 ) ? style : : cur_pointer : style : : cur_default ) ;
}
}
void EditorBlock : : setPressed ( int pressed ) {
if ( _pressed ! = pressed ) {
if ( _pressed > = 0 ) {
updateRow ( rowAtIndex ( _pressed ) ) ;
stopLastRipple ( _pressed ) ;
}
_pressed = pressed ;
if ( _pressed > = 0 ) {
addRowRipple ( _pressed ) ;
updateRow ( rowAtIndex ( _pressed ) ) ;
}
}
}
void EditorBlock : : addRowRipple ( int index ) {
auto & row = rowAtIndex ( index ) ;
auto ripple = row . ripple ( ) ;
if ( ! ripple ) {
2022-10-03 15:11:05 +04:00
auto mask = Ui : : RippleAnimation : : RectMask ( QSize ( width ( ) , row . height ( ) ) ) ;
2017-02-21 16:45:56 +03:00
ripple = row . setRipple ( std : : make_unique < Ui : : RippleAnimation > ( st : : defaultRippleAnimation , std : : move ( mask ) , [ this , index = findRowIndex ( & row ) ] {
2017-02-09 16:43:21 +03:00
updateRow ( _data [ index ] ) ;
2017-02-03 23:07:26 +03:00
} ) ) ;
}
auto origin = mapFromGlobal ( QCursor : : pos ( ) ) - QPoint ( 0 , row . top ( ) ) ;
ripple - > add ( origin ) ;
}
void EditorBlock : : stopLastRipple ( int index ) {
auto & row = rowAtIndex ( index ) ;
if ( row . ripple ( ) ) {
row . ripple ( ) - > lastStop ( ) ;
}
}
void EditorBlock : : updateRow ( const Row & row ) {
update ( 0 , row . top ( ) , width ( ) , row . height ( ) ) ;
}
void EditorBlock : : addRow ( const QString & name , const QString & copyOf , QColor value ) {
_data . push_back ( { name , copyOf , value } ) ;
_indices . insert ( name , _data . size ( ) - 1 ) ;
addToSearch ( _data . back ( ) ) ;
}
EditorBlock : : Row & EditorBlock : : rowAtIndex ( int index ) {
if ( isSearch ( ) ) {
return _data [ _searchResults [ index ] ] ;
}
return _data [ index ] ;
}
int EditorBlock : : findRowIndex ( const QString & name ) const {
2023-12-28 15:15:09 +03:00
return _indices . value ( name , - 1 ) ;
2017-02-03 23:07:26 +03:00
}
EditorBlock : : Row * EditorBlock : : findRow ( const QString & name ) {
auto index = findRowIndex ( name ) ;
return ( index > = 0 ) ? & _data [ index ] : nullptr ;
}
int EditorBlock : : findRowIndex ( const Row * row ) {
return row ? ( row - & _data [ 0 ] ) : - 1 ;
}
} // namespace Theme
} // namespace Window