Skip to content

Ability to set border edges independently #920

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

Closed
pbugnion opened this issue Nov 27, 2016 · 15 comments
Closed

Ability to set border edges independently #920

pbugnion opened this issue Nov 27, 2016 · 15 comments
Labels
resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion.
Milestone

Comments

@pbugnion
Copy link
Member

pbugnion commented Nov 27, 2016

If I'm not mistaken, widget.layout.border gets translated into adding the CSS border property. This property is short-hand for three separate border properties: border-width, border-style, and border-color. By forcing someone to use the shorthand border property, rather than the individual properties, there is no way to set different border types for the different border edges. There is no way (AFAICT), for instance, to build the equivalent of a CSS body like:

#mywidget {
  border-style: none none solid none; /* I just want a border at the bottom of my widget */
  border-width: 2px;
  border-color: black;
}

My use case is that I'm trying to build a table of widgets. Each row is an HBox of several widgets. It would be useful to specify that each row should have a bottom border.

I understand that I can obviously do this with a custom widget. Nevertheless, this seems like a fairly common use case. We could add three additional unicode traitlets to the Layout widget: border_style, border_width and border_color. We could also raise an exception if the user specifies both these and the shorthand border.

I'm very happy to submit a PR if people think this would be a useful addition.

@pbugnion pbugnion changed the title Ability to set border components independently Ability to set border edges independently Nov 27, 2016
@SylvainCorlay
Copy link
Member

@pbugnion thanks for opening the issue.

Whenever there is a shorthand for multiple properties (like border / margin / padding) this is the one that we have been exposing for the sake of simplicity. (So no border-[top/right/bottom/left]) properties were exposed.

Overall, I think that at least border-width should be added because it is part of the css box model.

@jasongrout thoughts?

@SylvainCorlay
Copy link
Member

SylvainCorlay commented Nov 27, 2016

Some quick technical background on this.

If you set border to 1px solid blue and then border-width to 2px, the border property is changed to 2px solid blue. If you modify border-width again to 2px 4px, the border property is changed to ''.

So shorthand properties are bound to properties in a complex fashion so that we cannot expose them both through traitlets.

Therefore, exposing border-width would require removing the border property... This would be a backward incompatible change...

@pbugnion
Copy link
Member Author

pbugnion commented Nov 27, 2016

Hmm... CSS is complicated. Thanks a lot for your help.

Would there be scope for making the backward incompatible change in the next release?

Alternatively, could we overload the border traitlet? For instance:

widget.layout.border = "2px solid blue" 
# => results in css like { border: 2px solid blue; }

widget.layout.border = { "style": "solid", "width" : "2px 4px", "color": "blue" }
# => results in css like { border-style: solid ; border-width: 2px 4px ; border-color : blue }

# OR

widget.layout.border = Border(style="solid", width="2px 4px", color="blue")
# where `Border` is a new Widget type.

I appreciate that this would be different to the current layout traitlets, which have a near 1-to-1 mapping with underlying CSS properties.

A very different option which would address my problem (but not the underlying issue) would be a new widget type for tables: something like the HBox / VBox widget, but aimed specially at creating tabular layouts of widgets. I suspect this would probably live in a separate repository, at least initially? For reference, this is my current widget -- it does everything I want, except I would like a black line under the headers:

screen shot 2016-11-27 at 14 26 37

@jasongrout
Copy link
Member

Would there be scope for making the backward incompatible change in the next release?

Yes, since it is a major release. However, I think we've been changing the way widgets can be styled pretty much every time we could for a long time - no doubt some people are getting tired of changing their code. But I think you're right that we don't have it right yet.

a new widget type for tables: something like the HBox / VBox widget, but aimed specially at creating tabular layouts of widgets.

I think this makes a lot of sense on its own. It's fragile to get a table layout from hbox/vbox combinations, I think.

It's a tricky thing to get the needs balanced between ease of use and power with regards to the styling.

@pbugnion
Copy link
Member Author

Thanks for your help!

Yes, since it is a major release. However, I think we've been changing the way widgets can be styled pretty much every time we could for a long time - no doubt some people are getting tired of changing their code. But I think you're right that we don't have it right yet.

Is that a tentative +1 for a PR deprecating border in favour of border_style, border_width and border_color?

For the tabular arrangement of widgets, would there be interest in including this in core ipywidgets, or should I set up another repository?

@SylvainCorlay
Copy link
Member

I just don't know how to deprecate without conflicting between the two implementations.

@pbugnion
Copy link
Member Author

pbugnion commented Nov 30, 2016

I guess one way would be to keep the border property synchronised with border_style, border_width and border_color at the traitlets level. Something like this:

import traitlets

class Layout(traitlets.HasTraits):
    
    border = traitlets.Unicode(None, allow_none=True)
    border_width = traitlets.Unicode(None, allow_none=True)
    border_style = traitlets.Unicode(None, allow_none=True)
    border_color = traitlets.Unicode(None, allow_none=True)
    
    @traitlets.validate('border')
    def _validate_border(self, proposal):
        # validate that this is a valid border property
        return proposal['value']
    
    @traitlets.observe('border')
    def _observe_border(self, change):
        new_border = change['new']
        width, style, color = new_border.split(' ')
        self.border_width = width
        self.border_style = style
        self.border_color = color
    
    @traitlets.observe('border_width')
    def _observe_width(self, change):
        new_width = change['new']
        new_width = new_width.strip()
        if len(new_width.split(" ")) > 1:
            # invalidate shorthand border property
            self.border = ""
        else:
            self.border = "{} {} {}".format(self.border_width, self.border_style, self.border_color)

l = Layout()
l.border = "2px solid blue"
l.border_width, l.border_style, l.border_color
# => (u'2px', u'solid', u'blue')

This is somewhat ugly since we would be re-implementing a lot of CSS validation at the widget layer. I suspect this would be a nightmare to maintain, but it would only need to be maintained correctly during the deprecation period.

@jasongrout jasongrout added this to the 6.0 milestone Jan 31, 2017
@SylvainCorlay
Copy link
Member

@jasongrout I would like to postpone this to after 6.0.

@SylvainCorlay SylvainCorlay removed this from the 6.0 milestone Feb 2, 2017
@jasongrout jasongrout added this to the Backlog milestone Feb 2, 2017
@AlJohri
Copy link

AlJohri commented Dec 31, 2017

I'm really interested in a table like widget and was attempting to make my own with HBox VBox rather unsuccessfully. I need to embed a button next to each row and I can't do that with a pandas dataframe.

screen shot 2017-12-30 at 9 46 21 pm

@pbugnion
Copy link
Member Author

pbugnion commented Dec 31, 2017

@AlJohri Thanks for the comment -- I've done something like that in a conference demo.

The demo stacks a bunch of widgets into HBoxes to make rows, then stacks the rows into a VBox to make a full table. It doesn't have buttons specifically, but should be easy enough to adapt.

Some third party table widget that takes care of the layout would be a welcome addition to the ecosystem.

@AlJohri
Copy link

AlJohri commented Dec 31, 2017

Thanks @pbugnion! I saw your repository after commenting. It looks like a great start. Were you able to figure out how to get the borders?

@pbugnion
Copy link
Member Author

pbugnion commented Dec 31, 2017

No, I don't think that's possible in pure Python. Depending on your level of commitment, you could add CSS classes to each HBox, and inject the CSS into the notebook directly.

import ipywidgets as widgets
from IPython.display import HTML

# In a separate cell so it gets displayed:
HTML("""
<style>
.bottom-border {
    border-bottom: 1px solid black
}
</style>
""")

# Build your widget and add a CSS class to it:
w = widgets.HBox([
    widgets.Label('t1'),
    widgets.Label('t2')
])
w.add_class('bottom-border')
w

screen shot 2017-12-31 at 06 16 21

@jasongrout
Copy link
Member

jasongrout commented Jan 2, 2018

A few more thoughts:

  1. now that CSS grid (https://caniuse.com/#feat=css-grid) is making its way into browsers, I think we could have (in core) a GridBox widget that places its widgets in a grid using the CSS grid spec.

  2. Perhaps the column heading underline can be part of the gridbox widget as a .style property?

@jasongrout
Copy link
Member

A GridBox widget is now #1942.

@pbugnion
Copy link
Member Author

pbugnion commented Jan 8, 2020

Closing in favour of #2689. Thanks @zerline !

@pbugnion pbugnion closed this as completed Jan 8, 2020
@lock lock bot added the resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion. label May 21, 2020
@lock lock bot locked as resolved and limited conversation to collaborators May 21, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion.
Projects
None yet
Development

No branches or pull requests

4 participants