Why does the Qt Stylesheet "Foo:hover * {" always apply? - c++

I have a list of items which I want to shade the currently selected one. The problem is that the Foo widget has children and when the following rule always applies instead of just on hover:
Foo:hover {
background-color:#00FFFF;
}
Foo:hover * {
background-color:#00FFFF;
}
How do I fix this?

Your syntax is wrong. It should be like this:
Foo *::hover{ background-color: #00FFFF; }
Or if you only want to apply this to direct children:
Foo > *::hover{ background-color: #00FFFF; }

Related

Why QTextDocument background color changes only once?

I am using TextEditor from the example provided with Qt (https://doc.qt.io/qt-5/qtquickcontrols1-texteditor-example.html). Here is the complete code https://code.qt.io/cgit/qt/qtquickcontrols.git/tree/examples/quickcontrols/controls/texteditor?h=5.15
I have created a custom method void DocumentHandler::setBackgroundColor(const QColor &color) to change the background color of the overall HTML document.
My problem is whenever I call a method to change the background color of QTextDocument using setDefaultStyleSheet, it is executed only once. ie, my document background changes only once. For the next calls, I can see the qDebug printing correctly, but the setDefaultStyleSheet doesn't work. However, everything works perfectly with normal text, only not with m_doc->toHtml().
How do I fix this?
If I change m_doc->setHtml("some random text"), it works as required
...
QColor DocumentHandler::backgroundColor() const
{
return m_backgroundColor;
}
void DocumentHandler::setBackgroundColor(const QColor &color)
{
m_backgroundColor = color.name(); // QColor variable
m_doc->setDefaultStyleSheet("body{background-color: '"+ color.name() +"'}"); // m_doc is QTextDocument *
m_doc->setHtml(m_doc->toHtml());
qDebug() << "BACKGROUND COLOR CHANGED" << color.name() ;
emit backgroundColorChanged();
}
In the QML, I have called it like this
...
DocumentHandlerModel {
id: document
target: textArea
cursorPosition: textArea.cursorPosition
selectionStart: textArea.selectionStart
selectionEnd: textArea.selectionEnd
backgroundColor: colorDialog2.color
onFontFamilyChanged: {
var index = Qt.fontFamilies().indexOf(document.fontFamily)
if (index === -1) {
fontFamilyComboBox.currentIndex = 0
fontFamilyComboBox.special = true
} else {
fontFamilyComboBox.currentIndex = index
fontFamilyComboBox.special = false
}
}
onError: {
errorDialog.text = message
errorDialog.visible = true
}
}
Cause
The documentation of QTextDocument::setDefaultStyleSheet says:
Note: Changing the default style sheet does not have any effect to the existing content of the document.
You try to overcome this by calling setHtml after setDefaultStyleSheet like that:
m_doc->setHtml(m_doc->toHtml());
However, this does not produce the desired result, because setDefaultStyleSheet actually embeds the background color in the HTML through the bgcolor CSS property.
To test this, add
m_doc->setHtml("<html><body><p>Test</p></body></html>");
qDebug() << m_doc->toHtml();
after
m_doc->setHtml(m_doc->toHtml());
The HTML content of m_doc from <html><body><p>Test</p></body></html>, when tested with #FF00FF, becames:
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;\" bgcolor=\"#ff00ff\">\n<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">Test</p></body></html>"
Note: Please, notice that assignment: bgcolor=\"#ff00ff\.
So, by writing m_doc->setHtml(m_doc->toHtml()); you effectively return the old color. That is why the color changes only the first time.
By the way, the widget now changes its color, but has lost its content.
Solution
It is hard to find an elegant solution, because in this case markup and styles are not kept separate, as in real HTML and CSS, but the style is embedded by Qt in the markup. The one thing which comes to my mind, is to parse the content of m_doc manually.
Note: Such a solution would be extremely fragile and I advise strongly against it. Maybe instead of using a stylesheet, change the background color by setting up the palette of the widget, which renders the content of the QTextDocument on the screen.
Note: In any case this does not seem like the intended behavior, at least it does not match the description in the documentation, so I would have reported it as a bug to Qt, if I were you.
Example
Here is an example I wrote for you in order to demonstrate the proposed solution:
void DocumentHandler::setBackgroundColor(const QColor &color)
{
const QString &bgcolor("bgcolor=\"");
QString html(m_doc->toHtml());
int n = html.indexOf(bgcolor, html.indexOf("<body"));
int k = n + bgcolor.length();
m_doc->setDefaultStyleSheet("body { background-color: '" + color.name() + "' }");
if (n >= 0)
html.replace(n + bgcolor.length(), html.mid(k, html.indexOf("\"", n + bgcolor.length()) - k).length(), color.name());
m_doc->setHtml(html);
m_backgroundColor = color.name(); // QColor variable
emit backgroundColorChanged();
}

Applying css style to Gtk::ToolButton is not working with a selector in gtkmm

I am trying to set an application wide css style in gtkmm-3.0 .This is how i initialize and load the style:
#include "MainWindow.h"
#include <string>
#include <iostream>
const std::string style = R"(
toolbutton {
border-color : #000000;
border-width : 1px;
border-radius: 0px;
}
)";
void loadStyle()
{
try
{
auto css = Gtk::CssProvider::create();
css->load_from_data(style);
Gtk::StyleContext::add_provider_for_screen(
Gdk::Screen::get_default(), css,
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
}
catch(const Gtk::CssProviderError error)
{
std::cerr << "Failed to load style:" <<error.code() << std::endl;
}
}
int main(int argc, char *argv[])
{
auto app = Gtk::Application::create(argc, argv,"com.test");
loadStyle();
MainWindow window(800, 600);
window.show_all();
return app->run(window);
}
MainWindow is just a Gtk::Window that has a Gtk::Toolbar with a few Gtk::ToolButton.
But for some reason the style isn't getting applied to my ToolButton's at all. If i change my stylesheet selector to "select all elements" it get's applied to my toolbuttons:
* {
border-color : #000000;
border-width : 1px;
border-radius: 0px;
}
So i am assuming my code is correct and the selector in my stylesheet is wrong.
However the documentation says GtkToolButton has a single CSS node with name toolbutton.
I am currently not setting any names or classes myself using set_name or add_class.
What am i doing wrong?
when we create Gtktoolbutton it's actually using Gtkbutton.
so in your css if you add
toolbutton button {
it will work.
gtkcss can be kinda confusing to learn as you wont find proper documentation(well I was not able to find a very detailed doc and all).. it's better to use GtkInspector to debug gtk css..

QTabWidget Hide and Show tabs

I have some problems with QTabWidget. In case of the missing Hide functionality I have to build my own. According to the documentation I use removeTab and insertTab, but with insert Tab I have a problem to show the Tab page that is removed.
I use to add
RibbonTabContent *ribbonTabContent = new RibbonTabContent;
QTabWidget::addTab(ribbonTabContent, tabIcon, tabName);
To remove is use:
void Ribbon::hideTab(const QString &tabName)
{
// Find ribbon tab
for (int i = 0; i < count(); i++)
{
if (tabText(i).toLower() == tabName.toLower())
{
QTabWidget::removeTab(i);
break;
}
}
}
Both functions are working, pWidget is always null. But now the insert function do not work well. I think there I have a problem, but do not understand my problem.
void Ribbon::showTab(const QString &tabName){
// Find ribbon tab
QWidget* pWidget= QTabWidget::findChild<RibbonTabContent *>(tabName);
if(pWidget){
QTabWidget::insertTab(2,pWidget, tabName);
}
}
Maybe someone can help me out?
If you call QTabWidget::removeTab you remove the tab at the specified index from the children tree of your QTabWidget, the tab instance is not actually deleted though, so when you search for that same tab with QTabWidget::findChild you can't find it because it's not a child of your QTabWidget anymore. From the code you show I think you probably would not find it anyway since findChild searches for a widget with the specified objectName but you never set it for your tab.
A solution would be to store the removed tabs and then restore them when you please.
Assuming m_hiddenTabs is a QHash<QString, QWidget*> or QMap<QString, QWidget*> you could try something like this.
void Ribbon::hideTab(const QString &tabName)
{
// Find ribbon tab
for (int i = 0; i < count(); i++)
{
if (tabText(i).toLower() == tabName.toLower())
{
m_hiddenTabs.insert(tabName.toLower(), QTabWidget::widget(i));
QTabWidget::removeTab(i);
break;
}
}
}
void Ribbon::showTab(const QString &tabName){
// Find ribbon tab
auto tab = m_hiddenTabs.take(tabName.toLower());
if(tab){
QTabWidget::insertTab(2, tab, tabName);
}
}
Since Qt 5.15 it is also possible to use setTabVisible:
void QTabWidget::setTabVisible(int index, bool visible)
If visible is true, the page at position index is visible; otherwise the page at position index is hidden. The page's tab is redrawn appropriately.If visible is true, the page at position index is visible; otherwise the page at position index is hidden. The page's tab is redrawn appropriately.
It is unfortunate that QTabBar is unable to 'hide' a tab.
Here is my very easy work-around: mark the tabs 'disabled' instead (e.g. ui->tabWidget->setTabEnabled(tabIndex, false);).
Then, use stylesheets to style the "disabled" tab as entirely invisible and taking up no space:
QTabBar::tab:disabled
{
min-width: 0px;
max-width: 0px;
color: rgba(0,0,0,0);
background-color: rgba(0,0,0,0);
}
This works near-perfectly for me, with the only downside being that you can't have both disabled and "hidden" tabs in the same tabbar. However, usually I want one or the other, not both in the same bar.

Change color of a Gtk::Entry stored in a specific variable

I am looking for a way to change the color of a Gtk::Entry that is stored in a specific variable. I am using the CSS way to specify the color of an Entry and I have found this code which changes the color of all entries in the application but this is not exactly what I am looking for:
styleContext = get_style_context();
provider = Gtk::CssProvider::create();
styleContext->add_provider_for_screen(Gdk::Screen::get_default(), provider,
GTK_STYLE_PROVIDER_PRIORITY_USER);
provider->load_from_data(".entry { background: red; }");
You can get the style context for that particular Gtk::Entry, it can look someting like:
auto style_context = entryWidget.get_style_context();
try {
auto red_background = Gtk::CssProvider::create();
red_background->load_from_data(" entry { background: red; } ");
style_context->add_provider(red_background, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
} catch (Gtk::CssProviderError& err) {
std::cerr << err.what() << "\n";
}
Sometimes it is tricky to have the style apply to the widget. If this is the case, try changing the selector from tag entry to *.
The API docs suggests it is possible use an id selector in the css and apply the css-provider to the parent Window. I have not been able to make this work.

figuring out sass mixins

I am brand new to sass and am trying to get my head wrapped around mixins and using if/else statements inside of mixins. I'm trying to write something simple that basically says if the width setting of a selector is greater than 50% it will be a certain color, otherwise it will be a different color.
Here is the code I have so far:
#mixin color-class($width) {
#if "$width > 50%" {
background-color: purple;
} #else {
background-color: orange;
}
}
.selector-two {
#include color-class(40%);
}
.selector-three {
#include color-class(80%);
}
The css outputs the color as purple no matter what value I put here so I'm definitely missing something, any help would be greatly appreciated.
try removing the ":
#if $width > 50% {}
You can quickly experiment with it here: http://sassmeister.com/