Skip to content

Commit

Permalink
feat(UI): Use flexbox for the UI
Browse files Browse the repository at this point in the history
This renders correctly on Firefox and will enable more flexible addition and removal of UI elements.
Based of the vtk.js VolumeController.
  • Loading branch information
thewtex committed Jan 31, 2018
1 parent 0df5e3f commit e4918da
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 84 deletions.
60 changes: 37 additions & 23 deletions src/ItkVtkImageViewer.mcss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,37 @@
100% { transform: rotate(360deg); }
}

.uiContainer {
display: flex;
align-items: stretch;
flex-direction: column;
justify-content: space-between;
position: absolute;
top: 5px;
left: 5px;
padding: 2px;
border: 0.5px solid black;
box-sizing: border-box;
z-index: 100;
}

.uiGroup {
background: rgba(128, 128, 128, 0.5);
border-radius: 4px;
margin: 2px;
z-index: 100;
}

.uiRow {
display: flex;
flex-direction: row;
flex: 1;
align-items: center;
justify-content: space-between;
padding: 2px;
}


.progress {
color: white;
font-size: 200%;
Expand All @@ -27,9 +58,7 @@
}

.piecewiseWidget {
position: absolute;
top: calc(10px + 1em);
left: 5px;
flex: 1;
background: rgba(255, 255, 255, 0.3);
border-radius: 5px;
z-index: 100;
Expand All @@ -42,15 +71,13 @@
height: 2em;
width: 2em;
cursor: pointer;
z-index: 100;
z-index: 80;
}

.toggleButton {
position: absolute;
top: 5px;
left: 5px;
height: 1em;
width: 1em;
flex: 1;
height: 1.5em;
width: 1.5em;
cursor: pointer;
z-index: 100;
}
Expand All @@ -60,23 +87,13 @@
}

.toggleButtonDarkBG {
position: absolute;
top: 5px;
left: 5px;
height: 1em;
width: 1em;
cursor: pointer;
z-index: 100;
composes: toggleButton;
filter: invert(100%);
-webkit-filter: invert(100%);
}

.selector {
position: absolute;
top: 5px;
left: 400px;
transform: translate(-100%, 0px);
flex: 1;
border: none;
background: transparent;
color: white;
Expand All @@ -98,9 +115,6 @@ select:focus {
}

.shadow {
position: absolute;
top: 5px;
left: calc(1em + 15px);
border: none;
background: transparent;
color: white;
Expand Down
10 changes: 7 additions & 3 deletions src/createViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ const createViewer = (
view.setBackground(config.backgroundColor);
view.resize();

userInterface.createToggleUI(rootContainer, config.isBackgroundDark);
userInterface.addLogo(container);

const uiContainer = document.createElement('div');
rootContainer.appendChild(uiContainer);
userInterface.createMainUI(uiContainer, config.isBackgroundDark);

let imageSource = null;
let lookupTable = null;
Expand All @@ -65,8 +69,8 @@ const createViewer = (
lookupTable.setPresetName('Viridis (matplotlib)');
piecewiseFunction = proxyManager.getPiecewiseFunction(dataArray.getName());

userInterface.createVolumeUI(
rootContainer,
userInterface.createImageUI(
uiContainer,
lookupTable,
piecewiseFunction,
representation,
Expand Down
128 changes: 70 additions & 58 deletions src/userInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,21 @@ import toggleIcon from './icons/toggle.svg';

const domElements = {};

function getPreset(name) {
return vtkColorMaps.find((p) => p.Name === name);
}

function getLocalStyle(cssClasses, isBackgroundDark) {
function getContrastSensitiveStyle(cssClasses, isBackgroundDark) {
const stylePostFix = isBackgroundDark ? 'DarkBG' : 'BrightBG';
const localStyle = {};
const contrastSensitiveStyle = {};
cssClasses.forEach((name) => {
localStyle[name] = style[`${name}${stylePostFix}`];
contrastSensitiveStyle[name] = style[`${name}${stylePostFix}`];
});
return localStyle;
return contrastSensitiveStyle;
}

// ----------------------------------------------------------------------------

function getRootContainer(container) {
const workContainer = document.querySelector('.content');
const rootBody = document.querySelector('body');
return container || workContainer || rootBody;
}

// ----------------------------------------------------------------------------

function createLoadingProgress(container) {
const myContainer = getRootContainer(container);

Expand All @@ -43,10 +35,8 @@ function createLoadingProgress(container) {
myContainer.appendChild(domElements.progressContainer);
}

// ----------------------------------------------------------------------------

function createPiecewiseWidget(
rootContainer,
uiContainer,
lookupTableProxy,
piecewiseFunctionProxy,
dataArray,
Expand Down Expand Up @@ -83,10 +73,14 @@ function createPiecewiseWidget(
transferFunctionWidget.addGaussian(0.5, 1.0, 0.5, 0.5, 0.4);
transferFunctionWidget.applyOpacity(piecewiseFunction);

domElements.widgetContainer = document.createElement('div');
domElements.widgetContainer.setAttribute('class', style.piecewiseWidget);
domElements.piecewiseWidgetContainer = document.createElement('div');
domElements.piecewiseWidgetContainer.setAttribute(
'class',
style.piecewiseWidget
);
domElements.piecewiseWidgetContainer.className += ' js-toggle';

transferFunctionWidget.setContainer(domElements.widgetContainer);
transferFunctionWidget.setContainer(domElements.piecewiseWidgetContainer);
transferFunctionWidget.bindMouseListeners();

// Manage update when opacity changes
Expand Down Expand Up @@ -114,11 +108,14 @@ function createPiecewiseWidget(
});

transferFunctionWidget.render();
rootContainer.appendChild(domElements.widgetContainer);
const transferFunctionWidgetRow = document.createElement('div');
transferFunctionWidgetRow.setAttribute('class', style.uiRow);
transferFunctionWidgetRow.appendChild(domElements.piecewiseWidgetContainer);
uiContainer.appendChild(transferFunctionWidgetRow);
}

function createColorPresetSelector(
rootContainer,
uiContainer,
lookupTableProxy,
renderWindow
) {
Expand All @@ -136,12 +133,12 @@ function createColorPresetSelector(
}

domElements.presetSelector.addEventListener('change', applyPreset);
rootContainer.appendChild(domElements.presetSelector);
uiContainer.appendChild(domElements.presetSelector);
domElements.presetSelector.value = lookupTableProxy.getPresetName();
}

function createUseShadowToggle(
rootContainer,
uiContainer,
volumeRepresentation,
renderWindow
) {
Expand All @@ -156,69 +153,91 @@ function createUseShadowToggle(
volumeRepresentation.setUseShadow(useShadow);
renderWindow.render();
});
rootContainer.appendChild(domElements.shadowContainer);
uiContainer.appendChild(domElements.shadowContainer);
}

function createToggleUI(rootContainer, isBackgroundDark) {
function addLogo(uiContainer) {
const logo = new Image();
logo.src = logoIcon;
logo.setAttribute('class', style.logo);
rootContainer.appendChild(logo);
uiContainer.appendChild(logo);
}

function toggleWidgetVisibility() {
if (domElements.widgetContainer.style.display === 'none') {
domElements.widgetContainer.style.display = 'block';
domElements.presetSelector.style.display = 'block';
domElements.shadowContainer.style.display = 'block';
function createMainUI(uiContainer, isBackgroundDark) {
uiContainer.setAttribute('class', style.uiContainer);

const mainUIGroup = document.createElement('div');
mainUIGroup.setAttribute('class', style.uiGroup);

const mainUIRow = document.createElement('div');
mainUIRow.setAttribute('class', style.uiRow);
mainUIGroup.appendChild(mainUIRow);

function toggleUIVisibility() {
const elements = uiContainer.querySelectorAll('.js-toggle');
let count = elements.length;
const toggleElementStyle = window.getComputedStyle(elements[0]);
const expanded = toggleElementStyle.getPropertyValue('display') === 'flex';
if (!expanded) {
while (count--) {
elements[count].style.display = 'flex';
}
} else {
domElements.widgetContainer.style.display = 'none';
domElements.presetSelector.style.display = 'none';
domElements.shadowContainer.style.display = 'none';
while (count--) {
elements[count].style.display = 'none';
}
}
}

const toggleButton = document.createElement('div');
const localStyle = getLocalStyle(['toggleButton'], isBackgroundDark);
const contrastSensitiveStyle = getContrastSensitiveStyle(
['toggleButton'],
isBackgroundDark
);
toggleButton.innerHTML = `<div class="${
localStyle.toggleButton
contrastSensitiveStyle.toggleButton
}">${toggleIcon}</div>`;
toggleButton.addEventListener('click', toggleWidgetVisibility);
rootContainer.appendChild(toggleButton);
toggleButton.addEventListener('click', toggleUIVisibility);
mainUIRow.appendChild(toggleButton);

uiContainer.appendChild(mainUIGroup);
}

function createVolumeUI(
container,
function createImageUI(
uiContainer,
lookupTableProxy,
piecewiseFunctionProxy,
volumeRepresentation,
dataArray,
renderWindow,
isBackgroundDark
) {
const rootContainer = getRootContainer(container);

createUseShadowToggle(rootContainer, volumeRepresentation, renderWindow);
const imageUIGroup = document.createElement('div');
imageUIGroup.setAttribute('class', style.uiGroup);

createColorPresetSelector(rootContainer, lookupTableProxy, renderWindow);
const shadowPresetRow = document.createElement('div');
shadowPresetRow.setAttribute('class', style.uiRow);
createUseShadowToggle(shadowPresetRow, volumeRepresentation, renderWindow);
createColorPresetSelector(shadowPresetRow, lookupTableProxy, renderWindow);
shadowPresetRow.className += ' js-toggle';
imageUIGroup.appendChild(shadowPresetRow);

createPiecewiseWidget(
rootContainer,
imageUIGroup,
lookupTableProxy,
piecewiseFunctionProxy,
dataArray,
renderWindow
);
}

// ----------------------------------------------------------------------------
uiContainer.appendChild(imageUIGroup);
}

function progressCallback(progressEvent) {
const percent = Math.floor(100 * progressEvent.loaded / progressEvent.total);
domElements.progressContainer.innerHTML = `${percent}%`;
}

// ----------------------------------------------------------------------------

function emptyContainer(container) {
if (container) {
while (container.firstChild) {
Expand All @@ -227,15 +246,11 @@ function emptyContainer(container) {
}
}

// ----------------------------------------------------------------------------

function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}

// ----------------------------------------------------------------------------

function createFileDragAndDrop(container, onDataChange) {
const myContainer = getRootContainer(container);

Expand Down Expand Up @@ -265,16 +280,13 @@ function createFileDragAndDrop(container, onDataChange) {
// ----------------------------------------------------------------------------

export default {
createColorPresetSelector,
addLogo,
createFileDragAndDrop,
createLoadingProgress,
createPiecewiseWidget,
createToggleUI,
createUseShadowToggle,
createVolumeUI,
createMainUI,
createImageUI,
domElements,
emptyContainer,
getPreset,
getRootContainer,
progressCallback,
};

0 comments on commit e4918da

Please sign in to comment.