LCD Manger – part 6: Pane’s width and height

Lets move on with our pane widget. We will fix width and height calculation.

Download source.

Pane width is maximum width from all widgets in it. But not only – we can’t forget about offset. So we need to compare offset + width.

Tests:


    def test_get_correct_width_when_autowidth_is_on(self):
        widget_label = label.Label(1, 0)
        widget_label_1 = label.Label(0, 2)
        widget_label.label = "names"
        widget_label_1.label = "eman"
        self.pane.add_widget(widget_label)
        self.pane.add_widget(widget_label_1)
        assert_equal(self.pane.width, 6)

    def test_get_correct_width_when_autowidth_is_off(self):
        self.pane.width = 7
        widget_label = label.Label(1, 0)
        widget_label_1 = label.Label(0, 2)
        widget_label.label = "names"
        widget_label_1.label = "eman"
        self.pane.add_widget(widget_label)
        self.pane.add_widget(widget_label_1)
        assert_equal(self.pane.width, 7)

Private function to recalculate pane width:


def _calculate_width(self):
    """calculate longest row in dictionary"""
    return max(item.width + item.position['x'] for item in self.widgets)

Now we can add this function:

    def recalculate_width(self):
        if self.autowidth:
            self.width = self._calculate_width()

And call it from add_widget.
Whenever we add widget, pane width will be recalculated. This is good. But what happens when inner widget changes it’s size ? This is something we sadly won’t catch 😦 I’m not sure how to put listener on each element in array, so for now I added function to manually recalculate width. We must call it when we change inner widget.

I’m not so sure if render function is returning what we really want. So lets check it, find test test_should_render_two_labels. See that we have output variable. But its content is wrong. It should be array with width equal rows. Change it to:

        output = [
            " name",
            "     ",
            "eman "
        ]

Now test will fail. Easiest way to fix current problem is to pad each row if necessary. Modify function _crop_to_display:


    def _crop_to_display(self, output):
        """prepare text to display by cropping it"""
        rows = [label[0:self.width].ljust(self.width, " ") for label in output]
        return rows

Yey! Test are green. But there is one big problem. We are padding with spaces so we are overwriting anything bellow current widget. It’s bad very bad. This test will show how bad it is:

    def test_will_render_overlapping_widgets(self):
        widget_label = label.Label(1, 0)
        widget_label_1 = label.Label(4, 0)
        widget_label.label = "123"
        widget_label_1.label = "456"
        self.pane.add_widget(widget_label)
        self.pane.add_widget(widget_label_1)
        output = [
            " 123456"
        ]
        assert_equal(self.pane.render(), output)

Run tests and see that we are in red. If you hopped for output like ‘    456’ and not ‘    ‘ you must be puzzled about this. Case is easy, we are changing width and as the result autowidth is disabled. This is unintended behavior.
First we need to fix problem with calculating width. Its quite simple just add one line to recalculate_width function. This will restore autowith upon width change after adding new widget.

    def recalculate_width(self):
        """recalculate width"""
        if self.autowidth:
            self.width = self._calculate_width()
            self.autowidth = True

After this we are in red, but red that we expected 🙂

What to to with transparency ? First we should have a char that will be used as transparent one. Best way to define it is in manager.py. Add


TRANSPARENCY = ' '

At the top. Next we need to change two lines in pane.py. Add import and find correct lines and replace with:

output[offset_y] = line.rjust(item.position['x'] + len(line), manager.TRANSPARENCY)
rows = [label[0:self.width].ljust(self.width, manager.TRANSPARENCY) for label in output]

After this update tests in test_pane.py. But we are still red !
I’m thinking that problem with transparency we will leave for later. Its becoming too complicated for quick fix.

We can control width but height is still out of reach. We need to do something with it.

Similar to width we start with tests:

def test_get_correct_height_when_autoheigh_is_on(self):
    widget_label = label.Label(1, 0)
    widget_label_1 = label.Label(0, 2)
    widget_label.label = "names"
    widget_label_1.label = "eman"
    self.pane.add_widget(widget_label)
    self.pane.add_widget(widget_label_1)
    assert_equal(self.pane.height, 3)

def test_get_correct_width_when_autowidth_is_off(self):
    self.pane.height = 7
    widget_label = label.Label(1, 0)
    widget_label_1 = label.Label(0, 2)
    widget_label.label = "names"
    widget_label_1.label = "eman"
    self.pane.add_widget(widget_label)
    self.pane.add_widget(widget_label_1)
    assert_equal(self.pane.height, 7)

Return to pane.py. We will add functions to calculate height, something similar to width ones.

def recalculate_height(self):
    """recalculate height"""
    if self.autoheight:
        self.height = self._calculate_height()
        self.autoheight = True

def _calculate_height(self):
    """calculate height"""
    return max([item.height + item.position['y'] for item in self.widgets])

Last thing to do is to add

self.recalculate_height()

under

self.recalculate_width()

And tests are green again.

Stop! Jenkins time!

Surprise! No semantical violations. Strange 🙂

Summary

This time we added width and height calculation.

Download source.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s