Gnome Extension: Change St.Icon image on hover? - gnome

So task is simple, but not for me - green Gnome Extensions Development noob. Of course I googled this, but didn't find something that can be helpful here.
extension.js:
https://pastebin.com/TqDVp8Yz - because 'your post mostly code'.
stylesheet.css:
.poppy-button {
background-size: 20px;
width: 20px;
height: 20px;
background-image: url("Resources/poppy.svg");
}
.poppy-button:active,
.poppy-button:focus,
.poppy-button:checked,
.poppy-button:hover {
background-image: url("Resources/poppy-active.svg");
}

I would avoid the CSS and connect to the "enter-event" and "leave-event" signals, since all StWidget elements are also ClutterActors.
myIcon = new St.Icon({ icon_name: "normal-icon-name"; });
myIcon.connect("enter-event", (widget) => {
widget.icon_name = "hover-icon-name";
});
myIcon.connect("leave-event", (widget) => {
widget.icon_name = "normal-icon-name";
});
On the other hand, your CSS might work if you set "track-hover" to true on the StIcon.
Pro-Amateur-Tip: add the "gjs" tag to you questions to pull in more help for Gnome Shell extensions.

Related

Adding content script will make pointer-events: none (chrome extension) [duplicate]

I wrote a Google Chrome extension, which popups a dialog with an autocomplete field and it's own style, but there are some sites where my CSS gets totally broken, which doesn't look very nice.
I know about isolating styles with iFrames, but in Google Chrome extension there is no way to isolate my HTML and CSS in this way. Another method is to wrap all my stuff into a separated div with it's own id and relative styles for that id, and I do so, but it seems that it doesn't work on some sites with "hard" tags style overloading or "!important" directives in the CSS code.
So, I want to know is there any way to really isolate my styles in z convenient way or it's my bad carma to overload every little CSS property to fix one or another style issue for each site?
By the way: I set up my manifest to load all the things at the "document_end", but I see it's not being applied to the stylesheets which is every time loaded whenever the DOM is ready.
At the time of asking the question, your only option was to either use iframes, or stylesheets with a very high specificity and explicitly set all properties that might affect styles. The last method is very cumbersome, because there will always be some property that is overlooked by you. Consequently, the only usable method for isolating stylesheets was to use iframes.
The solution to this problem -isolation of styles without iframes- is Shadow DOM (since Chrome 25). You can find a tutorial at HTML5 Rocks. For a real-world Chrome extension that uses Shadow DOM to isolate styles, see Display #Anchors (source code here).
As I've recently gone through the gauntlet of this issue, I want to share some information I think is valuable.
First, Rob W's answer is correct. Shadow DOM is the correct solution to this problem. However, in my case not only did I need CSS isolation, I also needed JavaScript events. For example, what happens if the user clicks a button that lives within the isolated HTML? This gets really ugly with just Shadow DOM, but we have another Web Components technology, Custom Elements, to the rescue. Except that as of this writing there is a bug in chrome that prevents custom element in chrome extensions. See my questions here and here and the bug here.
So where does that leave us? I believe the best solution today is IFrames, which is what I went with. The article shahalpk linked is great but it only describes part of the process. Here's how I did it:
First, create an html file and js file for your isolated widget. Everything inside these files will run in an isolated environment in an iframe. Be sure to source your js file from the html file.
//iframe.js
var button = document.querySelector('.my-button');
button.addEventListener('click', function() {
// do useful things
});
//iframe.html
<style>
/* css */
</style>
<button class='my-button'>Hi there</button>
<script src='iframe.js'></script>
Next, inside your content script create an iframe element in javascript. You need to do it in javascript because you have to use chrome.extension.getURL in order to grab your iframe html file:
var iframe = document.createElement('iframe');
iframe.src = chrome.extension.getURL("iframe.html");
document.body.appendChild(iframe);
And that's it.
One thing to keep in mind: If you need to communicated between the iframe and the rest of the content script, you need to chrome.runtime.sendMessage() to the background page, and then chrome.tabs.sendMessage from the background page back to the tab. They can't communicate directly.
EDIT: I wrote a blog post detailing everything I learned through my process, including a complete example chrome extension and lots of links to different information:
https://apitman.com/3/#chrome-extension-content-script-stylesheet-isolation
In case my blog goes down, here's the sources to the original post:
Blog post
Example source
Either use all
.some-selector {
all: initial;
}
.some-selector * {
all: unset;
}
or use Shadow DOM
Library
function Widget(nodeName, appendTo){
this.outer = document.createElement(nodeName || 'DIV');
this.outer.className = 'extension-widget-' + chrome.runtime.id;
this.inner = this.outer.createShadowRoot();
(appendTo || document.body).appendChild(this.outer);
}
Widget.prototype.show = function(){
this.outer.style.display = 'block';
return this;
};
Widget.prototype.hide = function(){
this.outer.style.display = 'none';
return this;
};
Usage
var myWidget = new Widget();
myWidget.inner.innerHTML = '<h1>myWidget</h1>';
You can access the widget contents via myWidget.inner and the outer via myWidget.outer.
Styles
/*
* Reset Widget Wrapper Element
*/
.extension-widget-__MSG_##extension_id__ {
background: none;
border: none;
bottom: auto;
box-shadow: none;
color: black;
cursor: auto;
display: inline;
float: none;
font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-size: inherit;
font-style: normal;
font-variant: normal;
font-weight: normal;
height: auto;
left: auto;
letter-spacing: 0;
line-height: 100%;
margin: 0;
max-height: none;
max-width: none;
min-height: 0;
min-width: 0;
opacity: 1;
padding: 0;
position: static;
right: auto;
text-align: left;
text-decoration: none;
text-indent: 0;
text-shadow: none;
text-transform: none;
top: auto;
vertical-align: baseline;
white-space: normal;
width: auto;
z-index: 2147483648;
}
/*
* Add your own styles here
* but always prefix them with:
*
* .extension-widget-__MSG_##extension_id__
*
*/
.extension-widget-__MSG_##extension_id__{
position: fixed;
top: 100px;
margin: 0 auto;
left: 0;
right: 0;
width: 500px;
}
.extension-widget-__MSG_##extension_id__::shadow h1 {
display: block;
margin: 0 auto;
padding: 20px;
background-color: yellow;
border: 10px solid green;
font-size: 20px;
text-align: center;
}
I recently created Boundary, a CSS+JS library to solve problems just like this. Boundary creates elements that are completely separate from the existing webpage's CSS.
Take creating a dialog for example. After installing Boundary, you can do this in your content script
var dialog = Boundary.createBox("yourDialogID", "yourDialogClassName");
Boundary.loadBoxCSS("#yourDialogID", "style-for-elems-in-dialog.css");
Boundary.appendToBox(
"#yourDialogID",
"<button id='submit_button'>submit</button>"
);
Boundary.find("#submit_button").click(function() {
// some js after button is clicked.
});
Elements within #yourDialogID will not be affected by the existing webpage. And find() function returns a regular jQuery DOM element so you can do whatever you want with it.
Hope this helps. Please let me know if you have any question.
https://github.com/liviavinci/Boundary
Use iframes. It's a workaround, but works fine.
Maxime has written an article on it.

Chrome Extension causing misbehavior with divs on active websites [duplicate]

I wrote a Google Chrome extension, which popups a dialog with an autocomplete field and it's own style, but there are some sites where my CSS gets totally broken, which doesn't look very nice.
I know about isolating styles with iFrames, but in Google Chrome extension there is no way to isolate my HTML and CSS in this way. Another method is to wrap all my stuff into a separated div with it's own id and relative styles for that id, and I do so, but it seems that it doesn't work on some sites with "hard" tags style overloading or "!important" directives in the CSS code.
So, I want to know is there any way to really isolate my styles in z convenient way or it's my bad carma to overload every little CSS property to fix one or another style issue for each site?
By the way: I set up my manifest to load all the things at the "document_end", but I see it's not being applied to the stylesheets which is every time loaded whenever the DOM is ready.
At the time of asking the question, your only option was to either use iframes, or stylesheets with a very high specificity and explicitly set all properties that might affect styles. The last method is very cumbersome, because there will always be some property that is overlooked by you. Consequently, the only usable method for isolating stylesheets was to use iframes.
The solution to this problem -isolation of styles without iframes- is Shadow DOM (since Chrome 25). You can find a tutorial at HTML5 Rocks. For a real-world Chrome extension that uses Shadow DOM to isolate styles, see Display #Anchors (source code here).
As I've recently gone through the gauntlet of this issue, I want to share some information I think is valuable.
First, Rob W's answer is correct. Shadow DOM is the correct solution to this problem. However, in my case not only did I need CSS isolation, I also needed JavaScript events. For example, what happens if the user clicks a button that lives within the isolated HTML? This gets really ugly with just Shadow DOM, but we have another Web Components technology, Custom Elements, to the rescue. Except that as of this writing there is a bug in chrome that prevents custom element in chrome extensions. See my questions here and here and the bug here.
So where does that leave us? I believe the best solution today is IFrames, which is what I went with. The article shahalpk linked is great but it only describes part of the process. Here's how I did it:
First, create an html file and js file for your isolated widget. Everything inside these files will run in an isolated environment in an iframe. Be sure to source your js file from the html file.
//iframe.js
var button = document.querySelector('.my-button');
button.addEventListener('click', function() {
// do useful things
});
//iframe.html
<style>
/* css */
</style>
<button class='my-button'>Hi there</button>
<script src='iframe.js'></script>
Next, inside your content script create an iframe element in javascript. You need to do it in javascript because you have to use chrome.extension.getURL in order to grab your iframe html file:
var iframe = document.createElement('iframe');
iframe.src = chrome.extension.getURL("iframe.html");
document.body.appendChild(iframe);
And that's it.
One thing to keep in mind: If you need to communicated between the iframe and the rest of the content script, you need to chrome.runtime.sendMessage() to the background page, and then chrome.tabs.sendMessage from the background page back to the tab. They can't communicate directly.
EDIT: I wrote a blog post detailing everything I learned through my process, including a complete example chrome extension and lots of links to different information:
https://apitman.com/3/#chrome-extension-content-script-stylesheet-isolation
In case my blog goes down, here's the sources to the original post:
Blog post
Example source
Either use all
.some-selector {
all: initial;
}
.some-selector * {
all: unset;
}
or use Shadow DOM
Library
function Widget(nodeName, appendTo){
this.outer = document.createElement(nodeName || 'DIV');
this.outer.className = 'extension-widget-' + chrome.runtime.id;
this.inner = this.outer.createShadowRoot();
(appendTo || document.body).appendChild(this.outer);
}
Widget.prototype.show = function(){
this.outer.style.display = 'block';
return this;
};
Widget.prototype.hide = function(){
this.outer.style.display = 'none';
return this;
};
Usage
var myWidget = new Widget();
myWidget.inner.innerHTML = '<h1>myWidget</h1>';
You can access the widget contents via myWidget.inner and the outer via myWidget.outer.
Styles
/*
* Reset Widget Wrapper Element
*/
.extension-widget-__MSG_##extension_id__ {
background: none;
border: none;
bottom: auto;
box-shadow: none;
color: black;
cursor: auto;
display: inline;
float: none;
font-family : "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-size: inherit;
font-style: normal;
font-variant: normal;
font-weight: normal;
height: auto;
left: auto;
letter-spacing: 0;
line-height: 100%;
margin: 0;
max-height: none;
max-width: none;
min-height: 0;
min-width: 0;
opacity: 1;
padding: 0;
position: static;
right: auto;
text-align: left;
text-decoration: none;
text-indent: 0;
text-shadow: none;
text-transform: none;
top: auto;
vertical-align: baseline;
white-space: normal;
width: auto;
z-index: 2147483648;
}
/*
* Add your own styles here
* but always prefix them with:
*
* .extension-widget-__MSG_##extension_id__
*
*/
.extension-widget-__MSG_##extension_id__{
position: fixed;
top: 100px;
margin: 0 auto;
left: 0;
right: 0;
width: 500px;
}
.extension-widget-__MSG_##extension_id__::shadow h1 {
display: block;
margin: 0 auto;
padding: 20px;
background-color: yellow;
border: 10px solid green;
font-size: 20px;
text-align: center;
}
I recently created Boundary, a CSS+JS library to solve problems just like this. Boundary creates elements that are completely separate from the existing webpage's CSS.
Take creating a dialog for example. After installing Boundary, you can do this in your content script
var dialog = Boundary.createBox("yourDialogID", "yourDialogClassName");
Boundary.loadBoxCSS("#yourDialogID", "style-for-elems-in-dialog.css");
Boundary.appendToBox(
"#yourDialogID",
"<button id='submit_button'>submit</button>"
);
Boundary.find("#submit_button").click(function() {
// some js after button is clicked.
});
Elements within #yourDialogID will not be affected by the existing webpage. And find() function returns a regular jQuery DOM element so you can do whatever you want with it.
Hope this helps. Please let me know if you have any question.
https://github.com/liviavinci/Boundary
Use iframes. It's a workaround, but works fine.
Maxime has written an article on it.

How to test style of nested component jest-styled-components

Im testing using react-testing-library and jest-styled-components.
I have a wrapper component that renders the styles of its child button dependant on a selected prop passed to it.
This is the code:
const selectedStyles = css`
background-image: url(../image);
background-position: center;
background-repeat: no-repeat;
border-color: ${color.grey6};
height: 38px;
width: 58px;
& span {
display: none;
}
`;
const ButtonWrapper = styled.div`
& button {
font-size: 15px;
line-height: 20px;
padding: 8px 12px;
${props =>
props.selected
? css`
${selectedStyles}
`
: ""}
&:hover,
:focus {
${props =>
props.selected
? css`
${selectedStyles}
`
: ""}
}
}
`;
and the test
test("it renders the correct styles when selected ", () => {
const { container } = render(<CheckButton selected>Add</CheckButton>);
const button = container.querySelector("button");
expect(button).toHaveStyleRule("background-position", "center");
});
but its failing with "Property 'background-position' not found in style rules" which is true for the original button, however when its parent is passed the selected prop this style applies.
I am also doing snapshot testing with the component however not testing the props getting passed brings the test coverage down.
Can anyone help?
In general as far as nested styles testing is concerned, I would recommend testing directly the nested element.
I personally haven't figured out a way to test nested styles using the .toHaveStyle(``); (not even a simple
a {
text-decoration: none;
}
)
so I ended up querying for the exact component I wanted to test, eg:
expect(screen.getByText(/text-within-the-child-component/i)).toHaveStyle(`
text-decoration: none;
`);
In your specific case I believe the way to go is to render your component in your test directly with the props that trigger the styles you want for each case (selected in your code example).
For those who are facing the same problem toHaveStyleRule accept a third "options" parameter after property and value where you can path a modifier:
test("it renders the correct styles when selected ", () => {
render(<CheckButton selected>Add</CheckButton>);
const button = container.querySelector("button");
expect(screen.getByText("Add").parentElement).toHaveStyleRule("background-position", "center", { modifier: 'button' });
});
Here I state on the fact that "Add" is the button text and its parent is the component ButtonWrapper.
By the way, you should avoid as much as possible using querySelector (here I'm using react testing library).
https://github.com/styled-components/jest-styled-components

Csslint: Background image was used multiple times

I have this in my css:
#media screen and (max-width:1200px) {
#cssmenu {
background:url(/public/system/assets/img/profile.png) no-repeat , url(/public/system/assets/img/bgprofile.jpg) repeat-x;
width: 100%;
}
}
#media screen and (max-width:970px) {
#cssmenu {
background:url(/public/system/assets/img/profile.png) no-repeat , url(/public/system/assets/img/bgprofile.jpg) repeat-x;
width: 150px;
}
}
I get an error with csslint task:
Background image '/public/system/assets/img/bgprofile.jpg' was used multiple times, first declared at line 753, col 3. Every background-image should be unique. Use a common class for e.g. sprites. (duplicate-background-images)
Is there a way to declare these images so that I don't get this error?
Edit (another case):
.linkmycars
{
background:url('/public/system/assets/img/sub.png') no-repeat right 20px, url('/public/system/assets/img/bglinkcars.png') repeat-x #ececec;
}
.addcars
{
background:url('/public/system/assets/img/add.png') no-repeat right 17px, url('/public/system/assets/img/bglinkcars.png') repeat-x #ececec;
}
And I get this error: [L651:C1]
Background image '/public/system/assets/img/bglinkcars.png' was used multiple times, first declared at line 628, col 1. Every background-image should be unique. Use a common class for e.g. sprites. (d
uplicate-background-images)
One of your rules here seems totally redundant. The rule under max-width: 970px is already true when under max-width: 1200px.
To recap, change it to:
#media screen and (max-width:1200px) {
#cssmenu {
background:url(/public/system/assets/img/profile.png) no-repeat , url(/public/system/assets/img/bgprofile.jpg) repeat-x;
width: 100%;
}
}
#media screen and (max-width:970px) {
#cssmenu {
width: 150px;
}
}
As for your edited question, you face a couple of options. Because you have different images, you can't combine the two rules there.
Option one: sprite sub.png and add.png together, then use background position to move them into position/out of sight. This would only work in some cases, and it's a bit of a mess, depending on the layout. I made kind of a lazy example, just so you understand what I mean. You will probably have to create a sprite with a lot of transparent space between sub.png and add.png: jsfiddle
Option two: easier but less semantic. Instead of using multiple backgrounds, use multiple elements. jsfiddle and example:
html:
<div class="tiles"><div class="linkmycars"></div></div>
<div class="tiles"><div class="addcars"></div></div>
css:
.tiles {
background: url(/public/system/assets/img/bgprofile.jpg) repeat-x;
}
.linkmycars, .addcars {
width: 100%;
height: 100%;
}
.linkmycars {
background: url('/public/system/assets/img/sub.png') no-repeat right 20px;
}
.addcars {
background: url('/public/system/assets/img/add.png') no-repeat right 17px;
}
Third option: don't worry too much about csslint. It's there to help you, not make you jump through hoops. Your code will work great either way.
Hope it helped.

gnome shell extension stylesheet not loading

i'm trying to develop a gnome shell extension and I've created the "Hello World" extension which is created automatically with gnome-shell-extension-tool --create-extension
It creates 3 files: example.js, metadata.json, stylesheet.css.
I reload gnome-shell and the extension works correcty. The problem is, the styling file is not working at all; here is the code:
// Sample extension code, makes clicking on the panel show a message
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
function _showHello() {
let text = new St.Label({ style_class: 'hello', text: "Hello, world!" });
let monitor = global.get_primary_monitor();
global.stage.add_actor(text);
text.set_position(Math.floor (monitor.width / 2 - text.width / 2), Math.floor(monitor.height / 2 - text.height / 2));
text.add_style_class_name("hello");
Mainloop.timeout_add(6000, function () { text.destroy(); });
}
// Put your extension initialization code here
function main() {
Main.panel.actor.reactive = true;
Main.panel.actor.connect('button-release-event', _showHello);
}
And here stylesheet.css:
/* Example stylesheet */
.hello {
font-size: 360px;
font-weight: bold;
color: #ffffff;
background-color: rgba(10,10,10,0.7);
border-radius: 5px;
}
I didn't know how to even get more information about this....any idea?
Try the following stylesheet:
.hello {
font-size: 36px;
font-weight: bold;
color: #ffffff;
background-color: rgba(10,10,10,0.7);
border-radius: 15px;
margin: 50px;
padding: 50px;
}
I've finally worked it out :D
The problem was that I had installed the "user-theme" extension wich creates conficts with the css management
Hope can help someone
P.S. I'm using ArchLinux 32bit version

Resources