Change color of a Gtk::Entry stored in a specific variable - c++

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.

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();
}

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.

Test if QToolTip is obsucred before displaying

I'm using a QWindow to contain some heavily optimized OpenGL code.
To facilitate user interaction I have a mode where data under the cursor is displayed in the tooltip.
This leads to a UX problem when another window obscures the one sending the tooltip event. In the following screenshot, the tooltip is draw on top of a Firefox window (which is undesired).
What is the paradigmatic solution? Is there a way to test if part of a window is obscured?
The function calling the tooltip looks something like:
if (!qIsNaN(value_under_cursor))
{
auto state = QApplication::applicationState();
auto text = QString::number(value_under_cursor, 'f', 3);
static QString old_value;
if (text != old_value)
{
auto static last_show = timestamp();
auto now = timestamp();
auto re_raster = ((now - last_show) >= ms_to_chrono(100));
if (re_raster)
{
QToolTip::showText(current_mouse_coordinates_in_global, text);
last_show = now;
}
}
old_value = text;
}
I don't know if there is a cross-platform way to test if specific region is obscured by another apps window, but you can:
check if your window is in focus (QWindow::isActive(), IIRC) and only show tooltip if it is, or
render tooltip using OpenGL.

Sitecore Set the Number of Components

Is it possible to set the number of components in placeholder?
We can add as many as components in placeholder by using "Add to here" in gray box even the component has been already added.
I'd like to say that
In plcaceholder named 'bodyArea', you can set only one component in 'bodyArea' placeholder and you will not add any other component additionally.
Is there anyway how to do this??
There could be many ways, but this is what I used before.
// Check the number of renderings in placeholder
public static bool numberOfRenderings(string placeholderName)
{
bool rendering = true;
var renderingReferences = Sitecore.Context.Item.Visualization.GetRenderings(Sitecore.Context.Device, true);
int renderingsInPlaceholder = renderingReferences.Where(r => r.Placeholder.EndsWith('/' + placeholderName, StringComparison.OrdinalIgnoreCase)).Count();
if (renderingsInPlaceholder > 1)
{
return rendering = false;
}
return rendering;
}
In View.cshtml
if (#yourObject.numberOfRenderings("your-placeholder-key")) {
#Html.Sitecore().Placeholder("your-placeholder-key")
}
else
{
#Html.Raw("<div>Only one rendering item is available in this placeholder.</div>")
}
here is a blog where is describing how to restrict number of allowed controls :
http://www.newguid.net/sitecore/2014/restricting-the-number-of-components-in-the-sitecore-page-editor/
Other solution is using rules :
http://dotnetmafia.com/blogs/kevin/archive/2013/07/10/placeholder-settings-rules.aspx

Qt dynamic properties not set when in if statement

I'm facing a strange issue in my Qt application... I have a Widget inheriting QLabel with the following stylesheet:
QLabel { padding: 10px ; }
QLabel[current-player=true] { background: blue ; }
QLabel:disabled { background: #eee ; }
And a method:
void MyWidget::updateInformation () {
this->setEnabled (m_player->isEnable ());
if (m_player->isCurrentPlayer ()) {
qDebug () << "Setting current player to true: " << PlayerInfo::toString (m_player->player ()) ;
this->setProperty ("current-player", true);
}
// this->setProperty ("current-player", true);
qDebug () << "Property current player: " << this->property ("current-player") ;
}
As you can see, I want to set the background of my widget to blue when the current-player property is true, so I have the conditions m_player->isCurrentPlayer().
I have a line commented, which was used to test if the property worked, and it did. When I uncomment the line, the background becomes blue.
What is strange is that my debug output is (when the line is commented):
Setting current player to true: "Player1"
Property current player: QVariant(bool, true)
Setting current player to true: "Player1"
Property current player: QVariant(bool, true)
As you can see, the execution goes inside the if statement because I see the Setting current player... output, and the current-player property is true, but the background stay white...
I don't understand my the code works when I set the property all the time and doesn't work if I set the property in a if statement which is taken.
If anyone as an idea, it'll help me a lot!
Thanks!
It's OK. Stylesheets are not recomputed when you change custom properties. Because of performance issues.
Solution: Call polish() and unpolish() to a widget with stylesheet.
P.S. I want to note, that usage of custom properties for such style customization is bad practice, because in case of complex styles it will cause UI lags.