#! /bin/sh
# -*- mode: scheme; coding: utf-8 -*-
exec guile -e main -s "$0" "$@"
!#


;;;;
;;;; Copyright (C) 2023
;;;; Free Software Foundation, Inc.

;;;; This file is part of GNU G-Golf

;;;; GNU G-Golf is free software; you can redistribute it and/or modify
;;;; it under the terms of the GNU Lesser General Public License as
;;;; published by the Free Software Foundation; either version 3 of the
;;;; License, or (at your option) any later version.

;;;; GNU G-Golf is distributed in the hope that it will be useful, but
;;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;;; Lesser General Public License for more details.

;;;; You should have received a copy of the GNU Lesser General Public
;;;; License along with GNU G-Golf.  If not, see
;;;; <https://www.gnu.org/licenses/lgpl.html>.
;;;;

;;; Commentary:

;; Note: this example requires guile-cairo, a patched verison of
;; guile-cairo:

;;	https://www.nongnu.org/guile-cairo/

;; It actually needs a patched version of guile-cairo, that contains the
;; following new interface (which is not in guile-cairo 1.11.2):

;;	cairo-pointer->context

;; If by the time you have access to and wish to try this example
;; guile-cairo hasn't been released and/or cairo-pointer->context still
;; isn't commited to the latest guile-cairo repository master branch,
;; get in touch with us on irc.libera.chat, channel #guile, or by email,
;; we'll guide you to manually patch your local version.

;;; Code:


(eval-when (expand load eval)
  (use-modules (oop goops))

  (default-duplicate-binding-handler
    '(merge-generics replace warn-override-core warn last))

  (use-modules (g-golf))

  (g-irepository-require "Gtk" #:version "4.0")
  (for-each (lambda (name)
              (gi-import-by-name "Gdk" name))
      '("Paintable"))

  (for-each (lambda (name)
              (gi-import-by-name "Gtk" name))
      '("Application"
        "ApplicationWindow"
        "Image")))

(add-to-load-path (dirname (current-filename)))
(use-modules (nuclear-icon))


(define %max-progress 500)
(define %pi (acos -1))


(define-class <nuclear-animation> (<gobject> <gdk-paintable>)
  (draw-background #:accessor !draw-background
                   #:init-keyword #:draw-background)
  ;; This slot stores the progress of the animation.  We just count
  ;; upwards until we hit %max-progress and then start from scratch.
  (progress #:accessor !progress #:init-value 0)
  ;; This slot holds the ID of the timer that updates our progress slot
  ;; value.  We need to keep track of it so that we can remove it.
  (source-id #:accessor !source-id))

(define-method (initialize (self <nuclear-animation>) initargs)
  (next-method)
  (set! (!source-id self)
        (g-timeout-add 10
                       (lambda ()
                         (set! (!progress self)
                               (modulo (+ (!progress self) 1) %max-progress))
                         (invalidate-contents self)
                         #t))))

(define-vfunc (snapshot-vfunc (self <nuclear-animation>) snapshot width height)
  ;; We call the procedure provided by the (nuclear-icon) module.
  (nuclear-snapshot snapshot
                    (if (!draw-background self)
                        '(0.9 0.75 0.15 1.0)	;; nuclear yellow
                        '(0 0 0 0))		;; transparent
                    '(0.0 0.0 0.0 1.0)		;; black
                    width
                    height
                    (* 2 %pi (/ (!progress self) %max-progress))))

(define-vfunc (get-current-image-vfunc (self <nuclear-animation>))
  ;; For non-static paintables, this virtual function needs to be
  ;; implemented.  It must return a static paintable with the same
  ;; contents as the one currently has.

  ;; This is why the rotation slot was added to the <nuclear-icon>
  ;; class, so we can just return a new <nuclear-icon> instance.
  (make <nuclear-icon>
    #:rotaton (* 2 %pi (/ (!progress self) %max-progress))))

(define-vfunc (get-flags-vfunc (self <nuclear-animation>))
  ;; This time, we cannot set the static contents flag because the
  ;; animation changes the contents. However, the size still doesn't
  ;; change, so report that flag.
  '(size))

(define-vfunc (finalize-vfunc (self <nuclear-animation>))
  (g-source-remove (!source-id self))
  ;; This vfunc must 'chain-up' - call the <nuclear-animation> parent
  ;; finalize virtual method.
  (next-vfunc))


(define (activate app)
  (let ((window (make <gtk-application-window>
                  #:title "Nuclear Animation"
                  #:default-width 300
                  #:default-height 200
                  #:application app))
        (nuclear (make <nuclear-animation> #:draw-background #t))
        (image (make <gtk-image>)))
    (set-from-paintable image nuclear)
    (set-child window image)
    (present window)))


(define (main args)
  (let ((app (make <gtk-application>
               #:application-id "org.gtk.example")))
    (connect app 'activate activate)
    (let ((status (g-application-run app args)))
      (exit status))))
