Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Color and font themes #6698

Closed
wants to merge 6 commits into from
Closed

Color and font themes #6698

wants to merge 6 commits into from

Conversation

zeule
Copy link
Contributor

@zeule zeule commented Apr 26, 2017

Hi @qbittorrent/frequent-contributors, hi @magao,

At first, I link here related discussion in #6363 and related PRs: #3936 #4057 #6156 #6186 #6196.
So, the idea is to let user customise colours and fonts. But instead of providing a UI panel with a list of widgets to set all the colors and fonts, I decided to take another approach and implement colour and font themes. Why?

  • Such an option tab with a long sheet of colors or a table would be anesthetic.
  • It is un-maintainable for user and would severely pollute our configuration file.
  • It is not trivial how to allow to select colours from different sources like direct color specification, application palette or other ones (see below).

On the other hand, themes in files encapsulate all this stuff, and while that does not simplify editing much, at least it happens outside of the app.

What are the main ideas for the colour themes? Most of these properties are modelled after X11 icon theme and KDE themes.

  1. A colour theme file is a file in INI-format (can be read via QSettings).
  2. One section contains general information like name, description. These can be localised (not fully implemented yet).
  3. Rest of the file contain a dictionary that maps color identifier to color values. Colour identifiers are things like TorrentState, LogType.
  4. A color value may be an explicit color notation (everything accepted by QColor::QColor(QString) and some other objects. They are stored as "Scheme:value" strings. There are two known schemes now: 'RGB' and 'QPalette'. Obviously, 'RGB' maps onto QColor::QColor(QString) and 'QPalette' maps onto QPalette::color().
  5. Color notations are expandable, new schemes can be registered at run-time.
  6. Theme may inherit another theme, then all missing colours will be loaded from the inherited theme (which can inherit another one in turn). This allows user to make small adjustments to the app look.
  7. Themes are identified by name. There are lists of directories to load themes from. A theme from the top of this list hides all other themes with the same name from other dirs.

Take a look at two supplied examples: 'Default (Light)' and 'Default (Dark)'. The latter one inherits the former one and modifies it. It is supposed that all the themes will inherit 'Default (Light)' at the end.

This allows us to extend themes, modifying only these two default ones and all user themes will inherit new values.

Plans:

  • Theme API for the application (includes theme file format and locations).
  • Colour themes API.
  • Font themes API.
  • Use colour themes in the application.
  • Use font themes in the application.
  • Theme selection via GUI.
  • Exporting themes into user files for editing and/or creating custom themes.
  • Color provider that takes Plasma/KDE settings.
  • Add color cache for transfers list.
  • Inform user when a theme references an absent provider (like Plasma color theme when app is compiled without Plasma support).

The new "Appereance" settings tab look:
qbt-appereance-settings-tab

Bundled theme files: https://gist.github.com/evsh/03e56bfb49ba3b014d1a7bc63f9e7e1c

Icons

Added some tune-ups for icons. The goal is to provide the following two possibilities: correlate state icons with the color theme (done) and make all icons monochrome (partially done). To implement those tasks, I propose to drop PNGs and use SVG icons. Then I implement a simple "search-and-replace" function to change icon colour, which is then used to colourise state icons and to de-colourise default theme. Right now the de-colourised icons use fixed colour (defined by application palette), but we may provide user a colour chooser.

Screenshots with monochrome icons follow.
Light theme:
qbt_monochrome_icons_light
qbt_monochrome_icons_options_light

Dark theme:
qbt_monochrome_icons
qbt_monochrome_icons_options

Please note the changed progress bar tooltip:
progressbar-tooltip

Here are two most significant problems with the icons:

  • We need an icon for the new settings tab.
  • The "slow_on/slow_off" icon is of terrible quality, but I'm not sure what to use as a replacement. Can use "speedometer" and flip it for the second state. But I can't get icon file path when an icon is loaded from theme, so in this case I have to obtain a pixmap and flip it, and this may create problems with HiDPI screens.

So, the question is: how to extend our icon theme?

@Chocobo1
Copy link
Member

Chocobo1 commented Apr 27, 2017

I expect themes to be used in WebUI too. That is why most of the code reside in base/theme.

I don't think WebUI matters much in this case, there are 3rd party interfaces to use.

I would prefer to have a separate app for the theme creating and editing

+1, something like a basic mockup of qbt.

Theme may inherit another theme

Doesn't this boost up the complexity? for devs, dependency cycles? for users also, imagine a creator shares one of his theme but the theme depends on another one he didn't share....
I don't think this is a good idea, most users can live without it.

Where these files should go?

I imagine there will be import button and maybe export button, then this shouldn't be an issue.

@zeule
Copy link
Contributor Author

zeule commented Apr 27, 2017

I don't think WebUI matters much in this case, there are 3rd party interfaces to use.

This would greatly simplify things.

Doesn't this boost up the complexity?

Yes, but advantages outweigh, I think. We can deal with the problem you mentioned in the following way: when a theme inherits a non-existent one, or inherits nothing, we can modify it on loading to inherit one of the base qBt themes. In this way the theme will continue to work somehow. But what we get in return is flexibility to add new elements to our base themes without breaking all the user ones.

Where these files should go?

I imagine there will be import button and maybe export button, then this shouldn't be an issue.

I meant not a theme file, but implementation of a new color scheme, like QPalette but for Plasma.

@zeule
Copy link
Contributor Author

zeule commented May 2, 2017

PR Updated: all files are now in src/gui/theme, initialisation is done by the MainWindow class. Added base font theme support. Open question: how to denote "Application default font" in the theme file? Just write and empty string as value or use a special keyword?

@zeule
Copy link
Contributor Author

zeule commented May 2, 2017

Before making the final push and polishing I would very much appreciate your opinions.

#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
bool Preferences::useSystemIconTheme() const
{
return value("Preferences/Appearance/useSystemIconTheme", true).toBool();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need "Preferences" in these paths? Why not just "Appearance/useSystemIconTheme" and so on?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, inherited code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@glassez glassez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too many coding style issues! Did you forget use uncrustify?

}

// Move RSS cookies to global storage
QList<QNetworkCookie> cookies = getNetworkCookies();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the following lines are unrelated here!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment should belong to #6375

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines was in this place earlier but they were removed in current master.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... Mistake during rebasing then, I guess.... Thanks.

@@ -28,15 +28,21 @@
* Contact : chris@qbittorrent.org
*/

#include "executionlog.h"

#include "guiiconprovider.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move these includes on bottom of Qt includes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK ,but I still think that it is better to place them in the following order:

  1. corresponding header file
  2. app includes
  3. Qt include
  4. STL and other standard includes

Why? Because standard library includes are of less importance, everybody knows what is inside , but what is much more important is app modules inter-dependencies, showed by app includes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Second point: system includes are usually not sensitive to the include order, while app includes might have implicit dependencies. Listing them closer to the start of the includes list helps to find such dependencies if any.

@@ -141,6 +146,10 @@ MainWindow::MainWindow(QWidget *parent)
{
m_ui->setupUi(this);

Theme::Serialization::registerGuiColorProviders();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, it is time to create explicit GUI component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean something like:
src/base/ui.h:

struct UI
{
    virtual void show() = 0;
}

and in `src/gui/gui.h'

#include "base/ui.h"
class GraphicalUI: public UI
{
  void show() override {
      MainWindow::show();
}
};

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR, of course...
I didn't think about implementation, I have the idea itself. Now we have MainWindow as GUI manager, but this is wrong approach.

@@ -30,6 +30,9 @@

#include "downloadedpiecesbar.h"

#include "theme/colorprovider.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

App includes should be after system/Qt includes.


namespace Log
{
enum MsgType : int;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum MsgType: int;


const QString Theme::ThemeInfo::sectionName = QLatin1String(GROUP_NAME);

Theme::ThemeInfo::ThemeInfo() = default;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not in declaration?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise it would be inlined, and we do not want it, right?

return debug;
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant blank line

* Description_uk=Description in Ukrainian
*
*/
class ThemeInfo {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brace


QDebug operator<<(QDebug debug, const ThemeInfo &info);


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant blank line

}
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant blank line

QList<QPair<QString, QString>> prefsToMigrate = {
{ "Preferences/General/Locale", "Preferences/Appearance/Locale" },
{ "Preferences/General/AlternatingRowColors", "Preferences/Appearance/AlternatingRowColors" },
{ "Preferences/Advanced/useSystemIconTheme", "Preferences/Appearance/useSystemIconTheme" },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@glassez , probably you suggest to replace names here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@zeule
Copy link
Contributor Author

zeule commented May 4, 2017

Here is another question. Consider font themes. Quite likely the default font theme should use default application font. In terms of QFont::fromString() this is an empty string. OK, then out default theme looks as follows:

[Info]
Name=Default
Description=Default qBittorrent font theme.

[Fonts]
TransferList=QFont:
TorrentProperties=QFont:
ExecutionLog=QFont:

which is not very good, because of the empty descriptions. @LordNyriox believes that a descriptive placeholder is better:

[Fonts]
TransferList=QFont:<default>
TorrentProperties=QFont:<default>
ExecutionLog=QFont:<default>

But here is another decision: when exporting a font theme, should we compare fonts to the default one and write empty string or the placeholder? Or should we write full font string? I tend towards the latter, because if user wants the default font, they can simply erase the description string from the theme. Or, we may check whether theme contains default fonts and ask user first how they need to be serialized: as full descriptions of as empty strings.

@glassez
Copy link
Member

glassez commented May 4, 2017

Why should default theme define something? It has only default values, isn't it?

@zeule
Copy link
Contributor Author

zeule commented May 4, 2017

Well, default colour theme define colours. Why default font theme can't?

@glassez
Copy link
Member

glassez commented May 4, 2017

I just mean we can omit default values...

@zeule
Copy link
Contributor Author

zeule commented May 4, 2017

If you suggest to omit entries with default values in the default theme files, then I disagree. It would perplex theme loading code and theme interface. Naturally, inherited values are omitted in a theme. But they are simply loaded from the inherited theme. Otherwise, the theme loading code (resides in the theme class now) needs to know what value is the default one, and while it is more or less obvious for fonts, there is no a single default value for colours. That is why I decided that it will be simpler for us to control default values via default theme files, but not from source code. Those theme files are bundled as resources and always present. The only thing code needs to know is the name of the default theme.

@glassez
Copy link
Member

glassez commented May 4, 2017

Then I don't know how the previous questions? If you need explicit values, then use them. What empty strings and placeholders?

@zeule
Copy link
Contributor Author

zeule commented May 4, 2017

If you need explicit values, then use them.

Because I also need "default application font" value.

OK, thank you for the discussion. I think to implement empty strings and optionally replace them with font descriptions when exporting a font theme.

@glassez
Copy link
Member

glassez commented May 5, 2017

Because I also need "default application font" value.

What do you mean by that?

@zeule
Copy link
Contributor Author

zeule commented May 6, 2017

PR updated: font themes are used in the app now. Implementation borrowed from #6196 and I thank @Zalewa for that.

@zeule
Copy link
Contributor Author

zeule commented May 6, 2017

Because I also need "default application font" value.

What do you mean by that?

Please see the current built-in font theme. It contains empty values which mean "application default font".

@Zalewa
Copy link

Zalewa commented May 6, 2017

Thanks for notification. I'm glad to know that this is being worked on.

Copy link
Contributor

@thalieht thalieht left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coding style mostly.

}

Theme::ThemeNotFoundException::ThemeNotFoundException(const QString& themeName)
: std::runtime_error("Could not file theme named '" + themeName.toStdString() + "'")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Could not find theme file"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, themes are addressed by theme name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about "Could not find a theme file named"? I could be wrong but
file theme doesn't seem right, theme file seems more correct. Anyway in the current one you forgot "Could not find file theme named"? Unless the class name is deceiving.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right! Thanks! There is a typo. Has to be "Could not find theme named".

#include "themeinfo.h"
#include "base/logger.h"
#include "base/profile.h"
#include "base/settingsstorage.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section should be at the end and base should be on top according to the guidelines.

@@ -39,9 +39,10 @@
#include "base/torrentfilter.h"
#include "base/utils/fs.h"
#include "torrentmodel.h"
#include "theme/colortheme.h"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant extra line.

@@ -58,6 +58,8 @@
#include "transferlistdelegate.h"
#include "transferlistsortmodel.h"
#include "updownratiodlg.h"
#include "theme/themeprovider.h"
#include "theme/fonttheme.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One line up.

#define QBT_THEME_SERIALIZABLECOLORTHEME_H

#include "colortheme.h"
#include "colorprovider_p.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One line up.

@@ -95,6 +95,11 @@
#include "lineedit.h"
#include "executionlog.h"
#include "hidabletabwidget.h"

#include "theme/themeprovider.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 lines down.

#include "guiiconprovider.h"
#include "loglistwidget.h"

#include "theme/themeprovider.h"
#include "theme/colortheme.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One line up.

#define QBT_THEME_THEMECOMMON_H

#include <QString>
#include <QLoggingCategory>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One line up.


namespace Theme
{
class SerializableFontTheme: public FontTheme {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bracket here and a few lines below empty line before private:

#define QBT_THEME_SERIALIZABLEFONTTHEME_H

#include "fonttheme.h"
#include "fontprovider_p.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One line up.

@zeule
Copy link
Contributor Author

zeule commented May 6, 2017

@thalieht: thanks, addressed!

@glassez
Copy link
Member

glassez commented May 7, 2017

Please see the current built-in font theme. It contains empty values which mean "application default font".

I still don't understand why you can't omit these entries instead of having empty values.

@zeule
Copy link
Contributor Author

zeule commented May 7, 2017

I still don't understand why you can't omit these entries instead of having empty values.

Suppose you are modifying a theme element or extending theme type. For example, you are renaming theme element. You have to rename it in base theme file and in the source code. And suppose you forgot to change its name in one of the places or mistyped it. With current approach you get error during theme loading, while in the opposite case you would silently get default font.

It is even more important, I think, that themes of both types behave uniformly, because there is no "default colour" and one may not omit colour elements.


if (!preValue.isNull()) {
qDebug() << "Migrating preference" << pre << "->" << post;
setValue(post, pre);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setValue(post, preValue);

@glassez
Copy link
Member

glassez commented May 14, 2017

@evsh, by the way, why not implement theme support using Qt style sheets?

@zeule
Copy link
Contributor Author

zeule commented May 14, 2017

Stylesheets change widget rendering engine, and the widgets do not look native anymore. While with this PR I try yo get more native look for qBt. However, this PR offers a way to add more theme types.

@zeule zeule changed the title [WIP] Color and font themes Color and font themes May 15, 2017
@silverqx
Copy link
Contributor

Should the dark theme work? I tried on win10 and ubuntu gnome and without success.

@zeule
Copy link
Contributor Author

zeule commented Sep 26, 2017

Please explain what does not work, I don't understand what do you mean by "dark theme". This PR controls only colours of the custom-drawn UI elements (i.e. the widget colours are controlled by a system style). The whole aim of this PR is to fit qBt internal colours into the existing colour/style environments.

@silverqx
Copy link
Contributor

I meant Options - Themes - Color theme - Default (Dark), I expected that when i select dark theme, it will look like screenshot in first post.

@zeule
Copy link
Contributor Author

zeule commented Sep 26, 2017

No, it will not, sorry. That is controlled by Qt Style.

@zero77
Copy link

zero77 commented Sep 26, 2017

@evsh
Will this be implemented in the web-ui.

@zeule
Copy link
Contributor Author

zeule commented Sep 26, 2017

@zero77: no, there are no such plans at the moment, and see the discussion above, please.

@silverqx
Copy link
Contributor

No, it will not, sorry. That is controlled by Qt Style.

So it for now change colors for TorrentState and Log messages, if I understand.

Missing icons for toolButtonExportColorTheme and toolButtonExportFontTheme and get this wanrings during compile:

..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'horizontalLayout_12' (QHBoxLayout) is already in use, defaulting to 'horizontalLayout_121'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'label_24' (QLabel) is already in use, defaulting to 'label_241'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'verticalLayout_25' (QVBoxLayout) is already in use, defaulting to 'verticalLayout_251'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'scrollArea_5' (QScrollArea) is already in use, defaulting to 'scrollArea_51'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'scrollAreaWidgetContents_5' (QWidget) is already in use, defaulting to 'scrollAreaWidgetContents_51'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'verticalLayout_26' (QVBoxLayout) is already in use, defaulting to 'verticalLayout_261'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'verticalLayout_21' (QVBoxLayout) is already in use, defaulting to 'verticalLayout_211'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'verticalSpacer_5' (QSpacerItem) is already in use, defaulting to 'verticalSpacer_51'.
..\..\qBittorrent\src\gui\optionsdlg.ui: Warning: The name 'horizontalLayout_10' (QHBoxLayout) is already in use, defaulting to 'horizontalLayout_101'.

Progress bar in General panel should reflect this color scheme too.

@zeule
Copy link
Contributor Author

zeule commented Sep 26, 2017

So it for now change colors for TorrentState and Log messages, if I understand.

Also for the download progress bar, i.e. all the custom qBt colours.

Progress bar in General panel should reflect this color scheme too.

Yes, it is covered too.

Missing icons for toolButtonExportColorTheme and toolButtonExportFontTheme

Thanks!

@glassez
Copy link
Member

glassez commented Oct 4, 2017

@evsh, this PR became unmaintainable. 13 commits (not so bad) and 315 changed files (!!!). GitHub is terribly slow here, and navigation is not working.
Maybe you should separate independent changes in another PR (I mean coding style fixes and so on)? We could merge them first.

@zeule
Copy link
Contributor Author

zeule commented Oct 8, 2017

@LordNyriox: thanks!

@zeule
Copy link
Contributor Author

zeule commented Oct 13, 2017

@LordNyriox: could you redo the status icons inversion with the updated icons, please?

@zeule
Copy link
Contributor Author

zeule commented Oct 13, 2017

@LordNyriox: thanks a lot! I don't need PNGs for this PR (which in fact drops PNG icons in favor of SVG files).

magao and others added 6 commits October 19, 2017 16:07
by @magao: Consolidate appearance options in their own tab.
by @evsh: Add preferences-desktop-theme icon, copied from fa-desktop
(Unicode f108) symbol of FontAwesome
1. Use SVG icons and drop png files
2. Add function to change SVG fill color and use it to produce
monochrome icon set.
3. Allow torrent state icons to be colored corresponding to state colors.
@Symbai
Copy link

Symbai commented Feb 16, 2018

Is this still being worked on?

@Adowrath
Copy link

Adowrath commented Mar 28, 2018

Guys, it's been quite some time of silence on this. Can we expect theming in any version, at all, considering it's been almost a year since the PR came up? I'd love the feature to be in qbit. I wish I could help even, but I don't have much knowledge of C++ and Qt. :/

@5ay3h
Copy link

5ay3h commented Apr 5, 2018

Will fix #6434

@zeule zeule closed this Jul 11, 2018
@danilaml
Copy link

Sad it's been closed. The simplistic yet informative uTorrent look is the only thing keeping me from switching to qBittorrent completely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet