(local lume (require :lib.lume)) (local style (require :core.style)) (local common (require :core.common)) (local View (require :core.view)) (local {: attach-imstate : textbutton} (require :editor.imstate)) (local SlideshowView (View:extend)) (fn SlideshowView.parse [slides] (var style nil) (icollect [_ slide (ipairs slides)] (icollect [_ elem (ipairs slide)] (match (type elem) (where :table elem.style) (do (set style elem) nil) :table (if elem.button (lume.merge style elem) elem) :string (lume.merge style {:text elem}))))) (fn slides-target [slides] (var target 0) (each [_ slide (ipairs slides)] (each [_ elem (ipairs slide)] (when elem.target (set target (+ target elem.target))))) target) (fn SlideshowView.new [self slides] (SlideshowView.super.new self) (attach-imstate self) (set self.slides slides) (set self.total-target (slides-target slides)) (set self.imagecache {}) (set self.islide 1) (set self.ielem 0) (set self.cleanup {}) (self:cleanup-slide) (self:advance)) (fn SlideshowView.cleanup-slide [self] (each [_ f (pairs self.cleanup)] (f)) (set self.cleanup {}) (set self.current-target (slides-target (lume.slice self.slides 1 self.islide))) (set self.sections (icollect [_ slide (ipairs self.slides)] (let [{: section} (or (lume.match slide #$1.section) {})] section))) (var isection-current 0) (set self.islide-to-isection (icollect [_ slide (ipairs self.slides)] (let [{: section} (or (lume.match slide #$1.section) {})] (when section (set isection-current (+ isection-current 1))) isection-current)))) (fn SlideshowView.next-slide [self] (set self.islide (if (>= self.islide (length self.slides)) 1 (+ self.islide 1))) (set self.ielem 0) (self:cleanup-slide) (self:advance)) (fn SlideshowView.prev-slide [self] (set self.islide (if (<= self.islide 1) (length self.slides) (- self.islide 1))) (set self.ielem (+ 1 (length (. self.slides self.islide)))) (self:cleanup-slide) (self:back)) (fn SlideshowView.ielemNext [self ielem di] (let [slide (. self.slides self.islide) elem (. slide ielem)] (when elem (when elem.action (if (= di 1) (tset self.cleanup ielem (elem:action)) (. self.cleanup ielem) ((. self.cleanup ielem)))) (if elem.pause-after ielem (self:ielemNext (+ ielem di) di))))) (fn SlideshowView.advance [self] (let [ielemNext (self:ielemNext (+ self.ielem 1) 1)] (if ielemNext (set self.ielem ielemNext) (self:next-slide)))) (fn SlideshowView.back [self] (let [ielemNext (self:ielemNext (- self.ielem 1) -1)] (if ielemNext (set self.ielem ielemNext) (self:prev-slide)))) (fn SlideshowView.load-image [self {:image filename}] (when (= (. self.imagecache filename) nil) (tset self.imagecache filename (love.graphics.newImage filename))) (. self.imagecache filename)) (fn SlideshowView.justify [self element width] (match element.justify :center (/ (- self.size.x width) 2) :right (- self.size.x width style.padding.x) _ style.padding.x)) (fn SlideshowView.this-y [self element y] (if element.topPadding (+ y element.topPadding) (+ y style.padding.y))) (fn SlideshowView.next-y [self element height y] (if element.lowerPadding (+ y height element.lowerPadding) element.overlay y (+ y height style.padding.y))) (fn SlideshowView.word-wrap [self element] (let [letter-width (element.font:get_width "m") screen-width (- self.size.x style.padding.x style.padding.x) max-letters (math.floor (/ screen-width letter-width)) wrapped (lume.wordwrap element.text max-letters) lines (icollect [line (string.gmatch wrapped "([^\n]+)")] line)] lines)) (fn SlideshowView.render-element [self element y] (if element.button (let [(pressed yNext) (textbutton self element.text (+ self.position.x (self:justify element (element.font:get_width element.text))) y element.font)] (when pressed (element:button)) (self:next-y element (- yNext y) y)) element.text (let [lines (self:word-wrap element) line-height (element.font:get_height) full-height (+ (* line-height (length lines)) (* style.padding.y (- (length lines) 1)))] (each [iline line (ipairs lines)] (let [width (element.font:get_width line) ;; todo: word-wrapping x (+ self.position.x (self:justify element width)) yline (+ y (* (+ (element.font:get_height) style.padding.y) (- iline 1)))] (renderer.draw_text element.font line x yline element.color))) (self:next-y element full-height y)) element.image (let [image (self:load-image element) x (+ self.position.x (self:justify element (image:getWidth)))] (love.graphics.setColor 1 1 1 element.alpha) (love.graphics.draw image x y) (self:next-y element (image:getHeight) y)) y)) (fn SlideshowView.draw [self] (self:draw_background style.background) (var y self.position.y) (each [ielem element (ipairs (. self.slides self.islide)) :until (> ielem self.ielem)] (set y (self:render-element element (self:this-y element y))))) ; timer (fn SlideshowView.elapsed [self] (if self.elapsed-time self.elapsed-time self.start-time (- (system.get_time) self.start-time) 0)) (fn SlideshowView.toggle-timer [self] (if (= self.start-time nil) (set self.start-time (system.get_time)) (= self.elapsed-time nil) (set self.elapsed-time (self:elapsed)) (do (set self.start-time (- (system.get_time) self.elapsed-time)) (set self.elapsed-time nil)))) (fn SlideshowView.reset-timer [self] (set self.elapsed-time nil) (set self.start-time nil)) (fn time [seconds] (let [sign (if (< seconds 0) "-" "") seconds (math.abs seconds) m (math.floor (/ seconds 60)) s (% seconds 60)] (string.format "%s%d:%02d" sign m s))) ; status bar (fn SlideshowView.status_items [self {: separator : separator2}] (let [full (renderer.font.load "presentation/font/PrintChar21.ttf" (* 14 SCALE)) thin (renderer.font.load "presentation/font/PRNumber3.ttf" (* 14 SCALE)) elapsed (self:elapsed) left [full "\xE2\x8C\xA5 " thin] right [thin (time (- self.total-target elapsed)) full " \xE2\x8C\x9B " thin (time (- self.current-target elapsed))]] (each [isection section (ipairs self.sections)] (when (> isection 1) (lume.push left style.dim " > ")) (lume.push left (if (= isection (. self.islide-to-isection self.islide)) style.text style.dim) section)) (values left right))) (fn SlideshowView.get_name [self] "] KFest 2021") SlideshowView