Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Making a horizontal dock

From MoodleDocs
Revision as of 08:08, 15 July 2021 by David Mudrak (talk | contribs) (Text replacement - "<code (.*)>" to "<syntaxhighlight lang="$1">")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Warning: This page is no longer in use. The information contained on the page should NOT be seen as relevant or reliable.


This quick tutorial looks at how to move the dock from its vertical position on the left hand side of the screen to a horizontal position at the top of the screen.
This modification will be done entirely within a theme through the creation of a small amount of JavaScript and a few lines of CSS.

Getting started

If you're new to Moodle and haven't yet read the tutorial on creating your first theme I strongly suggest you head there first.
I'd also strongly suggest you read Styling and customising the dock which explains the structure of the dock and a few of the things you will need to know in order to tackle this tutorial.

In order to make this tutorial easier to understand I am going to create a new theme that we'll call dockmod in which we will create this modification. So first step within your Moodle theme directory create a new directory called dockmod. Within this new directory we'll create two further directories JavaScript, and style. You should now have a directory structure like the one shown below:

moodle/theme/dockmod
moodle/theme/dockmod/javascript
moodle/theme/dockmod/style

The next step is to create the three files we are going to need, config.php, dockmod.js, and dockmod.css.

  1. config.php is of course for our theme's configuration and as you know an essential for any theme.
  2. dockmod.js will be created in the JavaScript directory, this file will contain the bit of JavaScript we need to write to get our modification to work.
  3. dockmod.css will be created in the style directory, this file will contain the CSS we need in order to make the dock horizontal.

You should end up with the following files:

moodle/theme/dockmod/config.php
moodle/theme/dockmod/javascript/dockmod.js
moodle/theme/dockmod/style/dockmod.css

config.php

In this step we will be setting up the config.php file for this theme, to be truthful there should be nothing new here for you, this is the easy part of the tutorial. The idea of this theme is to simply extend the standard theme and make the dock modification. This has the advantage that our theme will only consist of the JS and CSS required to achieve the dock modification we want.

So open the config.php file in your favourite editor and type the following in:

<?php

/**
 * This is the config file for the dockmod theme.
 */

$THEME->name = 'dockmod';
$THEME->sheets = array('dockmod');
$THEME->parents = array('standard', 'base');
$THEME->enable_dock = true;
$THEME->javascripts = array('dockmod');

So pretty simple really, the name of our theme is dockmod, you already knew that! The parents for this new theme are standard and base as explained above. We have one stylesheet also called dockmod (theme/dockmod/style/dockmod.css), and we have one JavaScript file also called dockmod (theme/dockmod/javascript/dockmod.js) and of course its essential we enable the dock, otherwise there's no point in creating this modification.

The JavaScript

OK this is where the tutorial starts to enter the [potentially] unknown.

The dock is pretty cool in that it looks for a specific JavaScript function when it loads and calls it if it exists, we can use this to modify the dock as we want. It also published several events that we are able to listen to in order to make further modifications when certain things happen.

In our case we want to make the dock horizontal. Now this isn't actually as nasty as it sounds because in a limited way the dock supports this! When I first wrote the dock I had in mind that one day I would have time to extend the dock to make it moveable, as such I wrote some basic support into it to allow the position and orientation to be switched. I know that a few of you in the community who have looked at making a horizontal dock have found these properties. Functions that rely on the position or orientation of the dock such as making the titles vertical have code within them that checks it makes sense to do so based upon the values of those properties. All in all this makes our modification a lot simpler as half the work is already done!

To start with open javascript/dockmod.js in your favourite editor and type the following:

function customise_dock_for_theme() {
    var dock = M.core_dock;
    dock.cfg.position = 'top';
    dock.cfg.orientation = 'horizontal';
}

And that is it; It sounds hard, but as you see really it is very easy (although as you get through this tutorial you will see we actually add a bit more JS later on).

So how this works:

function customise_dock_for_theme() { .... }

As mentioned above when the dock first loads it looks for a function and calls it, well this is the function customise_dock_for_theme.

var dock = M.core_dock;

Here we are collecting the dock into a variable. It is in this case available from the global M object.

dock.cfg.position = 'top';
dock.cfg.orientation = 'horizontal';

These two lines set two configuration variables for the dock, the position and the orientation. There are several other config properties that perhaps will be explored within another document or tutorial.

The CSS

If you save you changes presently and view your theme in your browser you will likely see a big mess. That is because while the JavaScript supports a horizontal dock there is not yet any CSS to support displaying the dock horizontally.

Because of this we will need to write a bit of CSS to ensure the dock is displayed correctly. Open style/dockmod.css in your favourite editor and type the following:

body.has_dock {margin-top:30px;margin-left:0;}
body.has_dock_top_horizontal #dock {width:100%;height:30px;background-image:url([[pix:theme|hgradient]]);background-repeat: repeat-x;border-right-width:0;border-bottom:1px solid #000;}
body.has_dock_top_horizontal #dock .dockeditem_container {margin-top:0;}
body.has_dock_top_horizontal #dock .dockeditem {display:inline-block;}
body.has_dock_top_horizontal #dock .dockeditem .dockedtitle {background-image:url([[pix:theme|hgradient]]);background-repeat: repeat-x;background-position:0 10%;border-style:solid;border-width: 0 1px 0 0;border-color:#AAA;}
body.has_dock_top_horizontal #dock .dockeditem .dockedtitle h2 {margin:0 12px;line-height:30px;}
body.has_dock_top_horizontal #dock .dockeditem.firstdockitem {margin-left:10px;}
body.has_dock_top_horizontal #dock .dockeditem.firstdockitem .dockedtitle {border-left-width:1px;}
body.has_dock_top_horizontal #dock .controls {bottom:auto;right:10px;top:0;width:auto;line-height:30px;}
body.has_dock_top_horizontal #dock #dockeditempanel {position:absolute;}

That's it!

The first line of CSS is to correct the margin on the body that accommodates the dock, we need to remove the left margin and increase the top margin. The other lines of CSS basically just correct change to horizontal images, change positioning from vertical to horizontal, and swap X/Y properties.

PLEASE NOTE: the above CSS using display:inline-block so this won't work in IE6/7, if this is a problem for anyone you could probably achieve something similar using floats.

Correcting a vertical bug

If you now open your site in your browser and switch to your theme you should see a horizontal dock. If you have a bit of a play with if you will notice that if you dock the settings block and fully expand it it will run off the bottom of the page and there will be no scroll bar. This has happened because we changed the position attributes of the dock in our CSS above. Essentially we could tinker with the CSS and try to fix it there, however based upon the design of the block and the complexities of getting it to work cross browser I can tell you it's going to be hard. Instead I'll show you how we can fix it in JavaScript.

Open your dockmod.js file again and type the following within the customize dock function below the three lines that are already in there.

dock.on('dock:resizepanelcomplete', theme_dockmod_handle_resize);

This line of code is adding an event listener to the dock so that when it fires the dock:resizepanelcomplete event the function theme_dockmod_handle_resize which we create shortly will get called.

The resizepanelcomplete function gets called after the panel that shows the content of a block has been resized. The panel gets resized any time it's display changes, e.g. when it is shown, or when the content within the panel changes (you expand something).
This event is perfect for our needs as when it is called we can look at the height of the panel and if it is more than the height of the screen we can add scrollbars to it and set the height so that the user can access all of the content of the panel.

Now we need to write the theme_dockmod_handle_resize function. Below the customize dock function type the following:

function theme_dockmod_handle_resize() {
    var dock = M.core_dock;
    var panel = dock.getPanel();
    var item = dock.getActiveItem();
    // Check its visible no point doing anything if its not.
    if (panel.visible === false || typeof(item) !== 'object') {
        return;
    }
    var buffer = dock.cfg.buffer;
    var screenheight = parseInt(dock.nodes.body.get('winHeight'))-(buffer*2)-dock.nodes.dock.get('offsetHeight');
    var scrolltop = panel.contentBody.get('scrollTop');
    // Reset the height of the panel so that we can accurately measure it
    panel.contentBody.setStyle('height', 'auto');
    // Remove the oversized class if it is there.
    panel.removeClass('oversized_content');
    var panelheight = panel.get('offsetHeight');
    // Set the height of the panel if required and add the oversized class
    if (panelheight > screenheight) {
        panel.contentBody.setStyle('height', (screenheight - panel.contentHeader.get('offsetHeight'))+'px');
        panel.addClass('oversized_content');
    }
    // Set the scrolltop of the panel to what it was before we started.
    if (scrolltop) {
        panel.contentBody.set('scrollTop', scrolltop);
    }
}

Save the file now, purge the Moodle cache, and reload the page. If you try to replicate the bug now you should find instead you get scrollbars and things work fine.

Congratulations, you've successfully created a horizontal dock!

Complete source

The following is the complete source files for this tutorial.

config.php

<?php

/**
 * This is the config file for the dockmod theme.
 */

$THEME->name = 'dockmod';
$THEME->sheets = array('dockmod');
$THEME->parents = array('standard', 'base');
$THEME->enable_dock = true;
$THEME->javascripts = array('dockmod');

dockmod.css

body.has_dock {margin-top:30px;margin-left:0;}

/** This is the CSS to display the block on the top of the screen */
.has_dock_top_horizontal #dock {width:100%;height:30px;background-image:url([[pix:theme|hgradient]]);background-repeat: repeat-x;border-right-width:0;border-bottom:1px solid #000;}
.has_dock_top_horizontal #dock .dockeditem_container {margin-top:0;}
.has_dock_top_horizontal #dock .dockeditem {display:inline-block;}
.has_dock_top_horizontal #dock .dockeditem .dockedtitle {background-image:url([[pix:theme|hgradient]]);background-repeat: repeat-x;background-position:0 10%;border-style:solid;border-width: 0 1px 0 0;border-color:#AAA;}
.has_dock_top_horizontal #dock .dockeditem .dockedtitle h2 {margin:0 12px;line-height:30px;}
.has_dock_top_horizontal #dock .dockeditem.firstdockitem {margin-left:10px;}
.has_dock_top_horizontal #dock .dockeditem.firstdockitem .dockedtitle {border-left-width:1px;}
.has_dock_top_horizontal #dock .controls {bottom:auto;right:10px;top:0;width:auto;line-height:30px;}
.has_dock_top_horizontal #dock #dockeditempanel {position:absolute;}

dockmod.js

function customise_dock_for_theme() {
    var dock = M.core_dock;
    dock.cfg.position = 'top';
    dock.cfg.orientation = 'horizontal';
    dock.on('dock:resizepanelcomplete', theme_dockmod_handle_resize);
}

function theme_dockmod_handle_resize() {
    var dock = M.core_dock;
    var panel = dock.getPanel();
    var item = dock.getActiveItem();
    // Check its visible no point doing anything if its not.
    if (panel.visible === false || typeof(item) !== 'object') {
        return;
    }
    var buffer = dock.cfg.buffer;
    var screenheight = parseInt(dock.nodes.body.get('winHeight'))-(buffer*2)-dock.nodes.dock.get('offsetHeight');
    var scrolltop = panel.contentBody.get('scrollTop');
    // Reset the height of the panel so that we can accurately measure it
    panel.contentBody.setStyle('height', 'auto');
    // Remove the oversized class if it is there.
    panel.removeClass('oversized_content');
    var panelheight = panel.get('offsetHeight');
    // Set the height of the panel if required and add the oversized class
    if (panelheight > screenheight) {
        panel.contentBody.setStyle('height', (screenheight - panel.contentHeader.get('offsetHeight'))+'px');
        panel.addClass('oversized_content');
    }
    // Set the scrolltop of the panel to what it was before we started.
    if (scrolltop) {
        panel.contentBody.set('scrollTop', scrolltop);
    }
}

Creating a moveable dock

A while ago I created theme that like this one supports a horizontal dock, however it goes one step further by adding a button above the undock all button that when clicked moves the down to the next position (without refreshing the page).

For those interested in having a look at that you can find it on my github account at: https://github.com/samhemelryk/moodle-theme_dockmod

See also