Use flexbox to align stuff

I don't know what you would need it for, but the manual position setting in the theme currently only works for the footer, won't be a hassle to do for main though.
This commit is contained in:
Daniel 2019-10-24 20:22:49 +00:00 committed by Tim Bentley
parent 66f19e05a1
commit 471ab7923d
4 changed files with 201 additions and 88 deletions

View File

@ -25,10 +25,18 @@ sup {
.reveal .slides > section, .reveal .slides > section,
.reveal .slides > section > section { .reveal .slides > section > section {
padding: 0; padding: 0;
max-height: 100%;
} }
.reveal > .backgrounds > .present { .reveal .slides > section.text-slides {
visibility: hidden !important; /* Need to override reveal styles to use our text aligment */
display: flex !important;
flex-direction: column;
word-wrap: break-word;
}
.reveal .slides > section p {
margin: 0;
} }
#global-background { #global-background {

View File

@ -28,19 +28,19 @@ var GradientType = {
* Horizontal alignment enumeration * Horizontal alignment enumeration
*/ */
var HorizontalAlign = { var HorizontalAlign = {
Left: "left", Left: 0,
Right: "right", Right: 1,
Center: "center", Center: 2,
Justify: "justify" Justify: 3
}; };
/** /**
* Vertical alignment enumeration * Vertical alignment enumeration
*/ */
var VerticalAlign = { var VerticalAlign = {
Top: "top", Top: 0,
Middle: "middle", Middle: 1,
Bottom: "bottom" Bottom: 2
}; };
/** /**
@ -349,7 +349,7 @@ var Display = {
* Start up reveal and do any other initialisation * Start up reveal and do any other initialisation
*/ */
init: function () { init: function () {
Reveal.initialize(this._revealConfig); Reveal.initialize(Display._revealConfig);
}, },
/** /**
* Reinitialise Reveal * Reinitialise Reveal
@ -369,13 +369,17 @@ var Display = {
*/ */
clearSlides: function () { clearSlides: function () {
$(".slides")[0].innerHTML = ""; $(".slides")[0].innerHTML = "";
this._slides = {}; Display._slides = {};
}, },
/** /**
* Checks if the present slide content fits within the slide * Checks if the present slide content fits within the slide
*/ */
doesContentFit: function () { doesContentFit: function () {
var currSlide = $(".slides")[0]; var currSlide = $("section.text-slides");
if (currSlide.length === 0) {
currSlide = $(".slides");
}
currSlide = currSlide[0];
console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight); console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight);
return currSlide.clientHeight >= currSlide.scrollHeight; return currSlide.clientHeight >= currSlide.scrollHeight;
}, },
@ -594,34 +598,35 @@ var Display = {
* Add a slide. If the slide exists but the HTML is different, update the slide. * Add a slide. If the slide exists but the HTML is different, update the slide.
* @param {string} verse - The verse number, e.g. "v1" * @param {string} verse - The verse number, e.g. "v1"
* @param {string} text - The HTML for the verse, e.g. "line1<br>line2" * @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
* @param {string} footer_text - The HTML for the footer" * @param {string} footer_text - The HTML for the footer
* @param {bool} reinit - True if React should reinit when creating a new slide
*/ */
addTextSlide: function (verse, text, footerText) { addTextSlide: function (verse, text, footerText, reinit=true) {
var slide; var slide;
var html = _prepareText(text); var html = _prepareText(text);
if (this._slides.hasOwnProperty(verse)) { if (Display._slides.hasOwnProperty(verse)) {
slide = $("#" + verse)[0]; slide = $("#" + verse)[0];
if (slide.innerHTML != html) { if (slide.innerHTML != html) {
slide.innerHTML = html; slide.innerHTML = html;
} }
} } else {
else { var parent = $("section.text-slides");
var slidesDiv = $(".slides")[0]; if (parent.length === 0) {
Display._createTextContainer();
parent = $("section.text-slides");
}
parent = parent[0];
slide = document.createElement("section"); slide = document.createElement("section");
slide.setAttribute("id", verse); slide.setAttribute("id", verse);
slide.innerHTML = html; slide.innerHTML = html;
slidesDiv.appendChild(slide); parent.appendChild(slide);
var slides = $(".slides > section"); Display._slides[verse] = parent.children.length - 1;
this._slides[verse] = slides.length - 1;
if (footerText) { if (footerText) {
$(".footer")[0].innerHTML = footerText; $(".footer")[0].innerHTML = footerText;
} }
} if (reinit) {
if ((arguments.length > 3) && (arguments[3] === true)) { Display.reinit();
this.reinit(); }
}
else if (arguments.length == 3) {
this.reinit();
} }
}, },
/** /**
@ -636,6 +641,19 @@ var Display = {
Display.reinit(); Display.reinit();
Display.goToSlide(0); Display.goToSlide(0);
}, },
/**
* Create the <section> that will contain text slides (vertical slides in react)
*/
_createTextContainer: function () {
var slideContainer = document.createElement("section");
slideContainer.classList = "text-slides";
var slidesDiv = $(".slides")[0];
slidesDiv.appendChild(slideContainer);
// Apply the current theme to the new container
if (!!Display._theme) {
Display.setTheme(Display._theme);
}
},
/** /**
* Set image slides * Set image slides
* @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}] * @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}]
@ -643,6 +661,7 @@ var Display = {
setImageSlides: function (slides) { setImageSlides: function (slides) {
Display.clearSlides(); Display.clearSlides();
var slidesDiv = $(".slides")[0]; var slidesDiv = $(".slides")[0];
var parentSection = document.createElement("section");
slides.forEach(function (slide, index) { slides.forEach(function (slide, index) {
var section = document.createElement("section"); var section = document.createElement("section");
section.setAttribute("id", index); section.setAttribute("id", index);
@ -652,9 +671,10 @@ var Display = {
img.src = slide.path; img.src = slide.path;
img.setAttribute("style", "max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);"); img.setAttribute("style", "max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);");
section.appendChild(img); section.appendChild(img);
slidesDiv.appendChild(section); parentSection.appendChild(section);
Display._slides[index.toString()] = index; Display._slides[index.toString()] = index;
}); });
slidesDiv.appendChild(parentSection);
Display.reinit(); Display.reinit();
}, },
/** /**
@ -662,7 +682,7 @@ var Display = {
* @param {Object} video - The video to show as a JS object: {"path": "url/to/file"} * @param {Object} video - The video to show as a JS object: {"path": "url/to/file"}
*/ */
setVideo: function (video) { setVideo: function (video) {
this.clearSlides(); Display.clearSlides();
var section = document.createElement("section"); var section = document.createElement("section");
section.setAttribute("data-background", "#000"); section.setAttribute("data-background", "#000");
var videoElement = document.createElement("video"); var videoElement = document.createElement("video");
@ -692,7 +712,7 @@ var Display = {
}); });
section.appendChild(videoElement); section.appendChild(videoElement);
$(".slides")[0].appendChild(section); $(".slides")[0].appendChild(section);
this.reinit(); Display.reinit();
}, },
/** /**
* Play a video * Play a video
@ -782,21 +802,21 @@ var Display = {
* @param slide The slide number or name, e.g. "v1", 0 * @param slide The slide number or name, e.g. "v1", 0
*/ */
goToSlide: function (slide) { goToSlide: function (slide) {
if (this._slides.hasOwnProperty(slide)) { if (Display._slides.hasOwnProperty(slide)) {
Reveal.slide(this._slides[slide]); Reveal.slide(0, Display._slides[slide]);
} }
else { else {
Reveal.slide(slide); Reveal.slide(0, slide);
} }
}, },
/** /**
* Go to the next slide in the list * Go to the next slide in the list
*/ */
next: Reveal.next, next: Reveal.nextFragment,
/** /**
* Go to the previous slide in the list * Go to the previous slide in the list
*/ */
prev: Reveal.prev, prev: Reveal.prevFragment,
/** /**
* Blank the screen * Blank the screen
*/ */
@ -835,20 +855,20 @@ var Display = {
* @param fontSize The font size in pts * @param fontSize The font size in pts
*/ */
calculateLineCount: function (fontSize) { calculateLineCount: function (fontSize) {
var p = $(".slides > section > p"); var p = $(".slides > section > section > p");
if (p.length == 0) { if (p.length == 0) {
this.addSlide("v1", "Arky arky"); Display.addSlide("v1", "Arky arky");
p = $(".slides > section > p"); p = $(".slides > section > section > p");
} }
p = p[0]; p = p[0];
p.style.fontSize = "" + fontSize + "pt"; p.style.fontSize = "" + fontSize + "pt";
var d = $(".slides")[0]; var d = $(".slides > section")[0];
var lh = parseFloat(_getStyle(p, "line-height")); var lh = parseFloat(_getStyle(p, "line-height"));
var dh = parseFloat(_getStyle(d, "height")); var dh = parseFloat(_getStyle(d, "height"));
return Math.floor(dh / lh); return Math.floor(dh / lh);
}, },
setTheme: function (theme) { setTheme: function (theme) {
this._theme = theme; Display._theme = theme;
// Set the background // Set the background
var globalBackground = $("#global-background")[0]; var globalBackground = $("#global-background")[0];
var backgroundStyle = {}; var backgroundStyle = {};
@ -912,44 +932,72 @@ var Display = {
globalBackground.innerHTML = backgroundHtml; globalBackground.innerHTML = backgroundHtml;
} }
// set up the main area // set up the main area
mainStyle = { mainStyle = {};
"word-wrap": "break-word",
/*"margin": "0",
"padding": "0"*/
};
if (!!theme.font_main_outline) { if (!!theme.font_main_outline) {
mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " + mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " +
theme.font_main_outline_color; theme.font_main_outline_color;
mainStyle["-webkit-text-fill-color"] = theme.font_main_color; mainStyle["-webkit-text-fill-color"] = theme.font_main_color;
} }
// These need to be fixed, in the Python they use a width passed in as a parameter
mainStyle.width = theme.font_main_width + "px";
mainStyle.height = theme.font_main_height + "px";
mainStyle.top = "" + theme.font_main_y + "px";
mainStyle.left = "" + theme.font_main_x + "px";
mainStyle.color = theme.font_main_color;
mainStyle["font-family"] = theme.font_main_name; mainStyle["font-family"] = theme.font_main_name;
mainStyle["font-size"] = "" + theme.font_main_size + "pt"; mainStyle["font-size"] = "" + theme.font_main_size + "pt";
mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : ""; mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : "";
mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : ""; mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : "";
mainStyle.color = theme.font_main_color;
mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%"; mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%";
mainStyle["text-align"] = theme.display_horizontal_align; // Using text-align-last because there is a <br> seperating each line
if (theme.display_horizontal_align != HorizontalAlign.Justify) { switch (theme.display_horizontal_align) {
mainStyle["white-space"] = "pre-wrap"; case HorizontalAlign.Justify:
mainStyle["text-align"] = "justify";
mainStyle["text-align-last"] = "justify";
break;
case HorizontalAlign.Center:
mainStyle["text-align"] = "center";
mainStyle["text-align-last"] = "center";
break;
case HorizontalAlign.Left:
mainStyle["text-align"] = "left";
mainStyle["text-align-last"] = "left";
break;
case HorizontalAlign.Right:
mainStyle["text-align"] = "right";
mainStyle["text-align-last"] = "right";
break;
default:
mainStyle["text-align"] = "center";
mainStyle["text-align-last"] = "center";
}
switch (theme.display_vertical_align) {
case VerticalAlign.Middle:
mainStyle["justify-content"] = "center";
break;
case VerticalAlign.Top:
mainStyle["justify-content"] = "flex-start";
break;
case VerticalAlign.Bottom:
mainStyle["justify-content"] = "flex-end";
// This gets around the webkit scroll height bug
mainStyle["padding-bottom"] = "" + (theme.font_main_size / 8) + "px";
break;
default:
mainStyle["justify-content"] = "center";
} }
mainStyle["vertical-align"] = theme.display_vertical_align;
if (theme.hasOwnProperty('font_main_shadow_size')) { if (theme.hasOwnProperty('font_main_shadow_size')) {
mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "px " + mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "pt " +
theme.font_main_shadow_size + "px"; theme.font_main_shadow_size + "pt";
} }
mainStyle["padding-bottom"] = theme.display_vertical_align == VerticalAlign.Bottom ? "0.5em" : "0"; var slidesDiv = $("section.text-slides");
mainStyle["padding-left"] = !!theme.font_main_outline ? "" + (theme.font_main_outline_size * 2) + "pt" : "0"; if (slidesDiv.length > 0) {
// These need to be fixed, in the Python they use a width passed in as a parameter slidesDiv = slidesDiv[0];
mainStyle.position = "absolute"; slidesDiv.style.cssText = "";
mainStyle.width = "" + (window.innerWidth - (theme.font_main_outline_size * 4)) + "px"; for (var mainKey in mainStyle) {
mainStyle.height = "" + (window.innerHeight - (theme.font_main_outline_size * 4)) + "px"; if (mainStyle.hasOwnProperty(mainKey)) {
mainStyle.left = "" + theme.font_main_x + "px"; slidesDiv.style.setProperty(mainKey, mainStyle[mainKey]);
mainStyle.top = "" + theme.font_main_y + "px"; }
var slidesDiv = $(".slides")[0];
slidesDiv.style.cssText = "";
for (var mainKey in mainStyle) {
if (mainStyle.hasOwnProperty(mainKey)) {
slidesDiv.style.setProperty(mainKey, mainStyle[mainKey]);
} }
} }
// Set up the footer // Set up the footer
@ -959,11 +1007,11 @@ var Display = {
footerStyle.position = "absolute"; footerStyle.position = "absolute";
footerStyle.left = "" + theme.font_footer_x + "px"; footerStyle.left = "" + theme.font_footer_x + "px";
footerStyle.top = "" + theme.font_footer_y + "px"; footerStyle.top = "" + theme.font_footer_y + "px";
footerStyle.bottom = "" + (window.innerHeight - theme.font_footer_y - theme.font_footer_height) + "px";
footerStyle.width = "" + theme.font_footer_width + "px"; footerStyle.width = "" + theme.font_footer_width + "px";
footerStyle.height = "" + theme.font_footer_height + "px";
footerStyle.color = theme.font_footer_color;
footerStyle["font-family"] = theme.font_footer_name; footerStyle["font-family"] = theme.font_footer_name;
footerStyle["font-size"] = "" + theme.font_footer_size + "pt"; footerStyle["font-size"] = "" + theme.font_footer_size + "pt";
footerStyle.color = theme.font_footer_color;
footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap"; footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap";
var footer = $(".footer")[0]; var footer = $(".footer")[0];
footer.style.cssText = ""; footer.style.cssText = "";

View File

@ -21,6 +21,9 @@
"test": "karma start --single-run", "test": "karma start --single-run",
"lint": "jshint openlp/core/display/html/display.js" "lint": "jshint openlp/core/display/html/display.js"
}, },
"jshintConfig": {
"esversion": 6
},
"author": "OpenLP Developers", "author": "OpenLP Developers",
"license": "GPL-3.0-or-later" "license": "GPL-3.0-or-later"
} }

View File

@ -147,7 +147,7 @@ describe("The Display object", function () {
Display._slides["v1"] = 0; Display._slides["v1"] = 0;
Display.goToSlide("v1"); Display.goToSlide("v1");
expect(Reveal.slide).toHaveBeenCalledWith(0); expect(Reveal.slide).toHaveBeenCalledWith(0, 0);
}); });
it("should have an alert() method", function () { it("should have an alert() method", function () {
@ -458,8 +458,8 @@ describe("Display.addTextSlide", function () {
Display.addTextSlide(verse, text, footer); Display.addTextSlide(verse, text, footer);
expect(Display._slides[verse]).toEqual(0); expect(Display._slides[verse]).toEqual(0);
expect($(".slides > section").length).toEqual(1); expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section")[0].innerHTML).toEqual(_prepareText(text)); expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).toHaveBeenCalled(); expect(Display.reinit).toHaveBeenCalled();
}); });
@ -472,8 +472,8 @@ describe("Display.addTextSlide", function () {
Display.addTextSlide(verse, text, footer, false); Display.addTextSlide(verse, text, footer, false);
expect(Display._slides[verse]).toEqual(0); expect(Display._slides[verse]).toEqual(0);
expect($(".slides > section").length).toEqual(1); expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section")[0].innerHTML).toEqual(_prepareText(text)); expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).not.toHaveBeenCalled(); expect(Display.reinit).not.toHaveBeenCalled();
}); });
@ -484,12 +484,12 @@ describe("Display.addTextSlide", function () {
Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false); Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false);
spyOn(Display, "reinit"); spyOn(Display, "reinit");
Display.addTextSlide(verse, text, true); Display.addTextSlide(verse, text, footer, true);
expect(Display._slides[verse]).toEqual(0); expect(Display._slides[verse]).toEqual(0);
expect($(".slides > section").length).toEqual(1); expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section")[0].innerHTML).toEqual(_prepareText(text)); expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).toHaveBeenCalled(); expect(Display.reinit).toHaveBeenCalledTimes(0);
}); });
}); });
@ -526,12 +526,12 @@ describe("Display.setTextSlides", function () {
expect(Display.clearSlides).toHaveBeenCalledTimes(1); expect(Display.clearSlides).toHaveBeenCalledTimes(1);
expect(Display._slides["v1"]).toEqual(0); expect(Display._slides["v1"]).toEqual(0);
expect(Display._slides["v2"]).toEqual(1); expect(Display._slides["v2"]).toEqual(1);
expect($(".slides > section").length).toEqual(2); expect($(".slides > section > section").length).toEqual(2);
expect(Display.reinit).toHaveBeenCalledTimes(1); expect(Display.reinit).toHaveBeenCalledTimes(1);
expect(Reveal.slide).toHaveBeenCalledWith(0); expect(Reveal.slide).toHaveBeenCalledWith(0, 0);
}); });
xit("should correctly set outline width (skipped for now)", function () { it("should correctly set outline width", function () {
const slides = [ const slides = [
{ {
"verse": "v1", "verse": "v1",
@ -549,14 +549,68 @@ describe("Display.setTextSlides", function () {
spyOn(Display, "reinit"); spyOn(Display, "reinit");
spyOn(Reveal, "slide"); spyOn(Reveal, "slide");
Display.setTextSlides(slides);
Display.setTheme(theme); Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".slides")[0]; const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['-webkit-text-stroke']).toEqual('42pt red'); expect(slidesDiv.style['-webkit-text-stroke']).toEqual('42pt red');
expect(slidesDiv.style['padding-left']).toEqual('84pt');
expect(slidesDiv.style['-webkit-text-fill-color']).toEqual('yellow'); expect(slidesDiv.style['-webkit-text-fill-color']).toEqual('yellow');
}) })
it("should correctly set text alignment,\
(check the order of alignments in the emuns are the same in both js and python)", function () {
const slides = [
{
"verse": "v1",
"text": "Amazing grace, how sweet the sound\nThat saved a wretch like me\n" +
"I once was lost, but now I'm found\nWas blind but now I see",
"footer": "Public Domain"
}
];
//
const theme = {
'display_horizontal_align': 3,
'display_vertical_align': 1
};
spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['text-align-last']).toEqual('justify');
expect(slidesDiv.style['justify-content']).toEqual('center');
})
it("should correctly set slide size position to theme size when adding a text slide", function () {
const slides = [
{
"verse": "v1",
"text": "Amazing grace, how sweet the sound\nThat saved a wretch like me\n" +
"I once was lost, but now I'm found\nWas blind but now I see",
"footer": "Public Domain"
}
];
//
const theme = {
'font_main_y': 789,
'font_main_x': 1000,
'font_main_width': 1230,
'font_main_height': 4560
};
spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['top']).toEqual('789px');
expect(slidesDiv.style['left']).toEqual('1000px');
expect(slidesDiv.style['width']).toEqual('1230px');
expect(slidesDiv.style['height']).toEqual('4560px');
})
}); });
describe("Display.setImageSlides", function () { describe("Display.setImageSlides", function () {
@ -578,12 +632,12 @@ describe("Display.setImageSlides", function () {
expect(Display.clearSlides).toHaveBeenCalledTimes(1); expect(Display.clearSlides).toHaveBeenCalledTimes(1);
expect(Display._slides["0"]).toEqual(0); expect(Display._slides["0"]).toEqual(0);
expect(Display._slides["1"]).toEqual(1); expect(Display._slides["1"]).toEqual(1);
expect($(".slides > section").length).toEqual(2); expect($(".slides > section > section").length).toEqual(2);
expect($(".slides > section > img").length).toEqual(2); expect($(".slides > section > section > img").length).toEqual(2);
expect($(".slides > section > img")[0].getAttribute("src")).toEqual("file:///openlp1.jpg") expect($(".slides > section > section > img")[0].getAttribute("src")).toEqual("file:///openlp1.jpg")
expect($(".slides > section > img")[0].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);") expect($(".slides > section > section > img")[0].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
expect($(".slides > section > img")[1].getAttribute("src")).toEqual("file:///openlp2.jpg") expect($(".slides > section > section > img")[1].getAttribute("src")).toEqual("file:///openlp2.jpg")
expect($(".slides > section > img")[1].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);") expect($(".slides > section > section > img")[1].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
expect(Display.reinit).toHaveBeenCalledTimes(1); expect(Display.reinit).toHaveBeenCalledTimes(1);
}); });
}); });