Sublime Forum

[Proof Of Concept] Visual Progress Bar


I’ve been meaning to work with mdpopups for a while, and decided to give it a shot when I came across this StackOverflow question asking how to implement a progress bar.

The implementation is pretty straightforward:

  • a loop with incrementing delay times that calls sublime.set_timeout
    ( Thanks @kingkeith for helping me out with it @ This Thread )   :grin:

  • an array of colors for mdpopups.color_box that is populated with 100 values

  • array entries up to the current progressPercent == progress color

  • array entries after the current progressPercent == incomplete color

  • if progressPercent == 100, all array entries == complete color




@ GitHub

( run the plugin by typing Progress Bar Demo @ the Command Palette )




I implemented sublime.load_resource to load a custom css file that alters the style of mdpopups.

All of the font-size properties updated correctly, but for some reason the color properties aren’t having any effect.

Any insight as to why?

Also, can you clarify how the location parameter works?   I know -1 sets it at the caret position, but am confused as to how it works since takes a single integer as opposed to an x and y value.

Awesome library by the way :slightly_smiling:


[SOLVED] How to execute a function immediately?
Interface Suggestions
[FORUM REQUEST] Resource Category / Pinned Thread Alternative

Are these not your colors?

I don’t usually use that parameter, but I think it corresponds to cursor position in the file (x), not mouse position (x, y). I pass that parameter directly to the Sublime API call, so Sublime is the one who implements the actual positioning of the tooltip, not me.



You know I think there may be a problem in the order in which I evaluate colors. Let me look into this.



Never mind. I think I know what your issue is. I’ll explain in bit when I have some time.

1 Like


wow, that looks awesome @fico! nice work <3

1 Like


Mdpops configures the colors from your color scheme. That is it’s first goal. It respects the users wishes first.

So here is the hierarchy of how the different CSS is evaluated.

  1. base.css is provided by mdpopups and is evaluated first. It sets up basic formatting.

  2. Plugin CSS that is passed in is evaluated second. This is mainly meant to override formatting for special cases and apply styling to new elements or new classes added to elements that aren’t already styled.

  3. User CSS is evaluated last. Mdpopups looks for this file in Users/mdpopups.css by default, but a default CSS if provided if it isn’t found. This provides a CSS template used to inject color scheme colors. It is evaluated last to respect users preferences above all else. The user expects mdpops to use their color scheme for consistency. I don’t want a plugin to come in and just start redefining a user’s expectations. Plugins can inject new spans with classes and divs with special classes etc. for more specialized formatting.

If you need to override colors, you can request that the user copy the default.css content to Users/mdpopups.css and then use a template command to include your CSS at the end: This gives them the option to accept your CSS or override it with their own.

It is recommended to documented all special classes you use so a user can override then if they choose.



Also, I do make a note under this section in the documentation about plugins and injecting new css. I talk about the importance of making name-spacing your injections:



One more thing. You can use raw HTML in markdown with style attributes if you must override something, but it is recommended to use CSS and give the user a choice.





Weird that it works for you, it didn’t work for me as you can see in the demo.
( note: those colors are just test values, don’t judge me :stuck_out_tongue: )


Do you think it’s possible to achieve window-relative positioning?

For example:
In my next mdpopups POC, I plan on creating an easy to implement timed popup notification for plugins which have commands with toggle functionality. ( like ScopeHunter, BracketHighlighter, etc. )

The popup will show something like "###FunctionName\n#ENABLED"
I think the best way to implement it would be to have it centered in the user screen.

This would be especially useful for toggle commands which are assigned to keybindings.

Also, I think your implementation may be document-relative.   While I was testing the POC, sometimes I would not be able to see the tootlip on execution; and I also noticed that it scrolls with the document.


I am not that familiar with CSS, and I can’t figure out how to implement what you mentioned at the moment.   I will have to spend some more time looking into it when I get a chance.


I would argue that the end user is much more likely to be unaware of mdpopups than the developer.
( implementation-wise )

For example:

A few weeks ago I had no idea what dependencies were. ( even after spending about a year with a strong focus on learning ST & having made a fairly involved plugin )

While browsing my Packages folder, I came across pygments, python-markdown, mdpopups, python-jinja2, & markupsafe & was like “dafuq is all that?!”

Now, after digging into the issue, I am aware of the benefits of dependencies & how they are implemented.   However, I don’t think casual users will have much of an expectation of something they may not understand or be interested in.  

I’ve been spending a lot of time on the ST forum & StackOverflow lately, and I’d say there are a good amount of people who just want something that works & are not very interested in digging into APIs & plugin resource files.

What happens if multiple developers take that approach? I am assuming it will be on the user to piece together sections of multiple files.


This is the method I was planning on implementing beyond the POC:

  • @ sublime-settings: "custom_mdpopups_CSS_File": true,
  • conditional statement @ to decide whether to assign css argument to the loaded resource or to None


I think an even better solution, which takes both developers and end users into account, would be:

@ mdpopups

  • use something like:
sublime.load_resource ( sublime.find_resources ( "Packages/PluginName/*mdpopups.css" )[0] )
  • if no file is found, use the default css file

###This would:

  • make it easier for developers to implement a custom setting by including mdpopups.css somewhere in their plugin directory

    • recommending the custom_mdpopups_CSS_File setting I mentioned above could help to set an easy to implement standard
  • an issue I anticipate with said method is in the case of a developer wanting different styles for different popups. ( maybe implement a workaround for such an issue?   perhaps a parameter that takes filenames with a prefix like: mdpopups_style1.css OR mdpopups.css and uses the default if the file is not matched )

  • make it easier for users to edit settings per-plugin, via something like PackageResourceViewer, while leaving the original custom css file intact

  • if they don’t like the style that a plugin author has provided, they can override it with their [ favorite | default ] file.

  • leave the default css file intact, which is beneficial in the case of updates on your end





This was because I commented out my user css content. It basically took away all the user overrides. It was a mistake on my part as it doesn’t reflect what you were doing.

Popups are view specific not window specific. Not exactly sure what you mean.

This is because -1 puts it at the cursor. If the cursor is off the screen, no popup.

The idea is that we don’t want popups that clash with the the themes. They blend into the themes because they grab the colors from the color scheme. Sometimes a developer needs to specially format an element with a specialized class (I do it in a couple of my plugins), so create the HTML elements with a class I can target with CSS. I try and stick to colors in the CSS theme where possible. But power users or just people who like to tweak everything (like me) can take control in their user file. I don’t want a plugin to come in and make my popups clash with my theme.

That is what I am hoping. Let me give you an example. Pretend this is my user css.

/* Just pretend all the default css stuff is here. */



/* etc Just append for every plugin.  Or they can give a snippet of css in their doc that they can directly post in here. */

See, very clean. And in each plugin css, they should be using namespaced class names. For instance: plugin1-randomclass and plugin2-randomclass. In this way plugin1 classes should never clash with plugin2 classes. If they do cause collisions, a user should open up an issue on their repo and tell them to namespace their classes and stop acting like their plugin is the only one that matters :).

ScopeHunter for instance uses special classes: They are namespaced, and if you don’t like them, you can override them in the User file.

I hope this makes a bit more sense.

1 Like


And to clarify, not all plugins have to use a separate CSS appended in the user css, mainly those that want to override already defined colors. ScopeHunter, ColorHelper, etc. don’t have to append anything to the user CSS and only inject CSS via the plugin because they are only doing format style changes or applying colors to spans with special classes. The default user css is only defining colors: You can change formatting for your plugin with no issues, but if you want to override the scheme colors, you have to append to the user CSS. But again the point of mdpopups is to use the color scheme colors so the popups fit theme, so when a plugin wants to override that, I am not too sympathetic as the plugin is trying break the framework instead of working within it.

1 Like


Thanks for putting up with my lengthy post :grin:

I’ve been messing around with it for the last hour or so.

So far what I have gotten working is:

@ Packages/User/mdpopups.css

/*Style with current theme.*/
{% if var.is_light %}
html{ {{'.background'|css('background-color')|brightness(0.9)}} }
{% else %}
html{ {{'.background'|css('background-color')|brightness(1.1)}} }
{% endif %}
h1, h2, h3, h4, h5, h6 { {{'.string'|css}} }
blockquote { {{'.comment'|css}} }
a { {{'.support.function'|css('color')}} }
hr { {{'.comment'|css('color')|background}} }
body { {{'.foreground'|css}}{{'.background'|css}} }
.admonition { {{ '.support.function'|css('color')|background|fade(0.8) }} }
.hint, .tip { {{ '.keyword'|css('color')|background|fade(0.8) }} }
.danger, .error { {{ ''|css('color')|background|fade(0.8) }} }
.important, .attention { {{ '.string'|css('color')|background|fade(0.8) }} }
.caution, .warning { {{ '.constant.numeric'|css('color')|background|fade(0.8) }} }
.note { {{ '.variable.language'|css('color')|background|fade(0.8) }} }
{% if var.use_pygments %}
{% if var.is_light %}
{% else %}
{% endif %}
{% else %}
{% if var.is_light %}
.highlight, .inline-highlight { {{'.background'|css|brightness(0.9)}} }
{% else %}
.highlight, .inline-highlight { {{'.background'|css|brightness(1.1)}} }
{% endif %}
{% endif %}



self.popupCSS = None

mdpopups.show_popup (
	view,               # view
	self.content,       # content
	True,               # markdown
	self.popupCSS,      # css
	0,                  # flags
	-1,                 # location
	self.popupWidth,    # width
	self.popupMaxHeight # height


This successfully overrides the color, but it appears to do so for all plugins.

My intention is to provide alternate theming for my plugin only, providing the user with the following setting @ MyPlugin.sublime-settings:

"mdpopupTheme": "Dark", // "Dark" | "Light" | "Default"

Dark & Light would activate css files included with my plugin, whereas Default would use the values @ mdpopups/css/default.css


I just re-read your post and noticed that you mentioned namespacing, which I did not include @ ProgressBarDemo_ProgressBar.css.

How would you call this from the plugin?
Your example @ ADD_CSS explicitly states the settings, but what if you just want to load a full css file?

As far as the default config goes, the only issues I had were that I felt the default text sizes are too small and that the default colors are getting pulled from some of the less desirable scopes of my theme.

Although I will note, ST should probably have an explicit ruler color value…
I’ve noticed a few other arbitrary relations as well.


What I meant was:

If I set location to 0, the popup is set to appear at the top of the view.   If I’m not scrolled to the top of the document, the popup will not appear.

I’m going to look into the ST documentation now, hopefully I can find a workaround like using viewport_extent to calculate the center x & y positions of the ST window.


Just found an answer by @FichteFoll.

Seems like it would be pretty tricky to implement what I had in mind, which is a progress indicator for a Macro Plugin I’m working on that spans multiple views.   I was hoping to figure out a way to implement absolute window positioning, irrespective of individual views.

Looks like I might have to go with r-stein’s solution to the original progress bar question for that specific implementation.



This successfully overrides the color, but it appears to do so for all plugins.

That is where namespacing your classes comes.

How would you call this from the plugin?
Your example @ ADD_CSS explicitly states the settings, but what if you just want to load a full css file?

My example didn’t need a CSS file as it was really small and I didn’t need it to come after the user CSS. I only needed it to come after the base.css formatting. So my example is run at the plugi step opposed to the User step like you did by including your CSS in the user CSS. Remember which CSS gets evaluated when:

  1. base
  2. plugin
  3. user/default

If you wanted to load css for injection (plugin step) from a CSS file opposed to just passing in a string as I did, you could do something like this and just pass that string in:

css = sublime.load_resource('my.css')

When you pass that in, mdpopups will clean it up by stripping out comments and carriage returns and pass it through the template engine. That will be loaded at the plugin step.

If you use this method: {{'Packages/ProgressBarDemo/ProgressBarDemo_ProgressBar.css'|getcss}}, mdpopups loads it and cleans it for you and it is loaded at the user step.

As far as the default config goes, the only issues I had were that I felt the default text sizes are too small and that the default colors are getting pulled from some of the less desirable scopes of my theme.

Size will very from machine to machine. High def screens are going have the text be too small. Low def screens it will seem fine. Maybe one day I will have a template variable for high and low def screens and I can abstract different sizes for different resolutions, but currently, it is what it is.

As for colors, yeah, that is the thing with color schemes. I can generate a theme from your color scheme, but I have no idea which colors you prefer from it as colors scopes from one theme may look great, and from another may be meh. But it will match your themes palette.

On the other hand, your approach will create preset tooltip palettes that may be pleasing together, but clash with various editor themes and schemes. Mdpopups started out with preset themes, but those themes didn’t always look great with some peoples editor themes. Yeah generating a theme from a color scheme isn’t perfect. It isn’t analyzing all the colors and applying color theory to come up with the most pleasing color pairs for text in a document, but it matches your editors theme and it isn’t too bad either. But that is also why having a user CSS file where user can tweak things for their specific theme is available. Mdpopups provides a reasonable solution out of the box in respect to color and size, but fine tuning the CSS in your user file can give you the best results.



If I ever needed a progress bar, I always just implemented it in the status bar. Whatever works though :).



sergioFC posted that exact solution at the same question.

I think it’s great for some less intense asyncronous processes, but if you’re running a super involved macro - it’s going to bog down ST completely until it finishes, so I’d like to have a fairly pronounced status indicator, rather than something as subtle as the status bar.

I’m going to play around with html settings for the popups for a bit.   The css stuff still seems a bit over my head, and the css = sublime.load_resource('my.css') method you mentioned is my original implementation which brought about my initial question to you about why the size properties were working but not color.

Thanks for taking the time to review my questions & providing a good amount of insight.
I’m sure as I work with css a bit more I can come back to this & understand some of it a bit better. :slightly_smiling:



Just remember that Sublime’s popup CSS engine is really limited. So you can’t do everything that you normally could do with CSS.

1 Like


Just remember, if you want to override anything in base.css, you can via plugin injection (like you did with css = sublime.load_resource('my.css')).

If you want to override anything in default.css, you will have to convince the user to import or manually add the required CSS after their User changes. Since you want to sidestep the whole coloring system (which is a feature of the plugin), it will require the user to accept your manual override by having them import it after their user settings.

You can always wrap your text with spans and add a special namespaced class. Those you can target with colors by injection as well since the default color rules don’t bother targeting things like spans.

Hit me up on the mdpopups repo via an issue if you have more questions. I am happy to discuss or help with anything you are confused about.

1 Like


If you end up using many callbacks, you might like to use my send_self decorator, which allows one to use generator methods to achieve linear-style programming using callback based methods.



I’m not familiar with linear programming, decorators, generators, proxies, or weak references.   I looked into them while reviewing your code, but they don’t seem like things I’d be able to grasp super quickly.

I’d likely have to spend some time digging into each & figuring out how to work with them independently before I can make sense of your code.

Can you post an ELI5 style explanation of what your code does?

Maybe a simple implementation of it @
( if you feel like it & have a few minutes to spare) :grin:



You could take a look at, which is based on @varriount’s idea (and code). I haven’t turned it into a pc dependency yet.