[the-board/sound-thing: 3/4] sound thing stuff



commit fefd37bffa96555ff61ff4770ac46db5f7c73c91
Author: Lucas Rocha <lucasr gnome org>
Date:   Wed Jan 5 00:49:44 2011 +0000

    sound thing stuff

 data/things/Makefile.am                      |    8 +
 data/things/sound/sound-controls-bg.png      |  Bin 0 -> 284 bytes
 data/things/sound/sound-progress-bar-bar.png |  Bin 0 -> 266 bytes
 data/things/sound/sound-progress-bar-bg.png  |  Bin 0 -> 236 bytes
 data/things/sound/style.css                  |   74 ++++
 src/js/ui/things/sound.js                    |  587 ++++++++++++++++++++++++++
 6 files changed, 669 insertions(+), 0 deletions(-)
---
diff --git a/data/things/Makefile.am b/data/things/Makefile.am
index 2b74f1a..36de402 100644
--- a/data/things/Makefile.am
+++ b/data/things/Makefile.am
@@ -19,6 +19,14 @@ dist_photo_DATA = \
     photo/style.css \
     photo/tape.png
 
+sounddir = $(thingsdir)/sound
+
+dist_sound_DATA = \
+    sound/style.css \
+    sound/sound-controls-bg.png \
+    sound/sound-progress-bar-bar.png \
+    sound/sound-progress-bar-bg.png
+
 videodir = $(thingsdir)/video
 
 dist_video_DATA = \
diff --git a/data/things/sound/sound-controls-bg.png b/data/things/sound/sound-controls-bg.png
new file mode 100644
index 0000000..0c66a1f
Binary files /dev/null and b/data/things/sound/sound-controls-bg.png differ
diff --git a/data/things/sound/sound-progress-bar-bar.png b/data/things/sound/sound-progress-bar-bar.png
new file mode 100644
index 0000000..2a36544
Binary files /dev/null and b/data/things/sound/sound-progress-bar-bar.png differ
diff --git a/data/things/sound/sound-progress-bar-bg.png b/data/things/sound/sound-progress-bar-bg.png
new file mode 100644
index 0000000..b5c944f
Binary files /dev/null and b/data/things/sound/sound-progress-bar-bg.png differ
diff --git a/data/things/sound/style.css b/data/things/sound/style.css
new file mode 100644
index 0000000..eef27db
--- /dev/null
+++ b/data/things/sound/style.css
@@ -0,0 +1,74 @@
+TbBox#sound-thing-sound-box {
+    spacing: 1px;
+    padding: 2px 2px 2px 2px;
+    background-color: black;
+}
+
+TbBox#sound-thing-content-box {
+    spacing: 5px;
+    padding: 2px 2px 5px 2px;
+    background-color: black;
+}
+
+TbBox#sound-thing-button-box {
+    background-color: #00000066;
+    padding: 5px;
+    spacing: 10px; 
+}
+
+TbBox#sound-thing-playback-box {
+    background-color: white;
+}
+
+TbBox#sound-thing-controls-box {
+    background-color: none;
+    padding: 15px;
+}
+
+TbBox#sound-thing-sound-controls-box {
+    border-image: url('sound-controls-bg.png') 8;
+    padding: 6px 12px 6px 12px;
+    spacing: 10px; 
+}
+
+MxButton#sound-thing-play-button {
+    background-color: none;
+    border-image: none;
+    padding: 0px;
+    -mx-icon-name: play;
+    -mx-icon-size: 24;
+}
+
+MxButton#sound-thing-play-button:checked {
+    -mx-icon-name: pause;
+}
+
+TbBox#sound-thing-sound-border-box {
+    border-image: none;
+    background-color: none;
+}
+
+MxLabel#sound-thing-caption-label {
+    color: white;
+    font-size: 13;
+    font-family: "Action Man";
+}
+
+MxProgressBar#sound-thing-progress-bar {
+  border-image: url('sound-progress-bar-bg.png') 4 4 6 4;
+}
+
+MxProgressBar#sound-thing-progress-bar MxProgressBarFill {
+  border-image: url('sound-progress-bar-bar.png') 3;
+  height: 12;
+}
+
+MxLabel#sound-thing-time-label {
+    color: white;
+    font-size: 15;
+    font-family: "Action Man";
+}
+
+TbBox#sound-thing-time-label-box {
+    padding: 4px 0px 0px 0px;
+}
diff --git a/src/js/ui/things/sound.js b/src/js/ui/things/sound.js
new file mode 100644
index 0000000..60412eb
--- /dev/null
+++ b/src/js/ui/things/sound.js
@@ -0,0 +1,587 @@
+// standard imports
+const Gettext = imports.gettext.domain("the-board");
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const Tweener = imports.tweener.tweener;
+
+// gi imports
+const Tb = imports.gi.Tb;
+const Clutter = imports.gi.Clutter;
+const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
+const Mx = imports.gi.Mx;
+const Pango = imports.gi.Pango;
+
+// ui imports
+const Thing = imports.ui.thing;
+const ToolBox = imports.ui.toolBox;
+const Toolbar = imports.ui.toolbar;
+
+// util imports
+const Path = imports.util.path;
+
+const NAME = Gettext.gettext("Sound");
+const STYLE = Path.THINGS_DATA_DIR + "sound/style.css";
+
+const _INITIAL_WIDTH = 200;
+const _INITIAL_HEIGHT = 100;
+
+const _SHOW_BUTTON_BOX_TIME = 0.5;
+
+function SoundThing(args) {
+    this._init(args);
+}
+
+SoundThing.prototype = {
+    __proto__: Thing.Thing.prototype,
+
+    _init : function(args) {
+        args = args || {};
+
+        args.content = this;
+        args.canResize = false;
+
+        this._style = new Mx.Style();
+        this._style.load_from_file(STYLE);
+
+        this._createSoundBox();
+        this._createPlaybackBox();
+        this._createCaptionText();
+
+        Thing.Thing.prototype._init.apply(this, [args]);
+    },
+
+    _createSoundBox : function() {
+        this._soundBox =
+            new Tb.Box({ orientation: Tb.BoxOrientation.HORIZONTAL,
+                         xAlign: Tb.BoxAlignment.FILL,
+                         yAlign: Tb.BoxAlignment.FILL,
+                         name: "sound-thing-sound-box" });
+
+        this._soundBox.set_style(this._style);
+
+        this._createContentBox();
+    },
+
+    _createContentBox : function() {
+        this._contentBox =
+            new Tb.Box({ orientation: Tb.BoxOrientation.VERTICAL,
+                         xAlign: Tb.BoxAlignment.FILL,
+                         yAlign: Tb.BoxAlignment.FILL,
+                         name: "sound-thing-content-box" });
+
+        this._contentBox.set_style(this._style);
+
+        this._soundBox.append(this._contentBox,
+                                Tb.BoxPackFlags.EXPAND);
+    },
+
+    _createPlaybackBox : function() {
+        this._playbackBox =
+            new Tb.Box({ orientation: Tb.BoxOrientation.VERTICAL,
+                         xAlign: Tb.BoxAlignment.FILL,
+                         yAlign: Tb.BoxAlignment.FILL,
+                         name: "sound-thing-playback-box" });
+
+        this._playbackBox.set_style(this._style);
+
+        this._createSoundControlsBox();
+
+        this._contentBox.append(this._playbackBox,
+                                Tb.BoxPackFlags.EXPAND);
+    },
+
+    _createSoundControlsBox : function() {
+        // This box is needed to add proper spacing between the
+        // sound controls and the edge of sound playback frame
+        let controlsBox =
+            new Tb.Box({ orientation: Tb.BoxOrientation.HORIZONTAL,
+                         xAlign: Tb.BoxAlignment.FILL,
+                         yAlign: Tb.BoxAlignment.CENTER,
+                         name: "sound-thing-controls-box" });
+
+        controlsBox.set_style(this._style);
+
+        this._soundControlsBox =
+            new Tb.Box({ orientation: Tb.BoxOrientation.HORIZONTAL,
+                         xAlign: Tb.BoxAlignment.FILL,
+                         yAlign: Tb.BoxAlignment.CENTER,
+                         opacity: 0,
+                         visible: false,
+                         name: "sound-thing-sound-controls-box" });
+
+        this._soundControlsBox.set_style(this._style);
+
+        this._createPlayButton();
+        this._createProgressBar();
+        this._createTimeLabel();
+
+        controlsBox.append(this._soundControlsBox,
+                           Tb.BoxPackFlags.EXPAND);
+
+        this._playbackBox.append(controlsBox,
+                                 Tb.BoxPackFlags.FIXED);
+
+        this._playbackBox.set_fixed_child_align(controlsBox,
+                                                Tb.BoxAlignment.FILL,
+                                                Tb.BoxAlignment.END);
+    },
+
+    _createPlayButton : function() {
+        this._playButton =
+            new Mx.Button({ isToggle: true,
+                            name: "sound-thing-play-button" });
+
+        this._playButton.set_style(this._style);
+
+        this._playButton.connect("notify::toggled",
+                                 Lang.bind(this,
+                                           this._onPlayButtonToggled));
+
+        this._soundControlsBox.append(this._playButton,
+                                      Tb.BoxPackFlags.NONE);
+    },
+
+    _createProgressBar : function() {
+        this._progressBar =
+            new Mx.ProgressBar({ reactive: true,
+                                 height: 12,
+                                 name: "sound-thing-progress-bar" });
+
+        this._progressBar.set_style(this._style);
+
+        this._updateProgressBar();
+
+        this._progressBar.connect("button-press-event",
+                                  Lang.bind(this,
+                                            this._onProgressBarButtonPressEvent));
+
+        this._soundControlsBox.append(this._progressBar,
+                                      Tb.BoxPackFlags.EXPAND);
+
+        this._soundControlsBox.set_child_align(this._progressBar,
+                                               Tb.BoxAlignment.FILL,
+                                               Tb.BoxAlignment.CENTER);
+    },
+
+    _createTimeLabel : function() {
+        this._timeLabel =
+            new Mx.Label({ yAlign: Mx.Align.MIDDLE,
+                           text: "00:12:00",
+                           anchorY: -2,
+                           name: "sound-thing-time-label" });
+
+        this._timeLabel.set_style(this._style);
+
+        this._updateTimeLabel();
+
+        this._soundControlsBox.append(this._timeLabel,
+                                      Tb.BoxPackFlags.END);
+
+        this._soundControlsBox.set_child_align(this._timeLabel,
+                                               Tb.BoxAlignment.START,
+                                               Tb.BoxAlignment.CENTER);
+    },
+
+    _createCaptionText : function() {
+        this._captionLabel =
+            new Mx.Label({ xAlign: Mx.Align.MIDDLE,
+                           name: "sound-thing-caption-label" });
+
+        this._captionLabel.set_style(this._style);
+
+        this._captionLabel.clutterText.lineAlignment = Pango.Alignment.CENTER;
+
+        this._captionLabel.connect("key-press-event",
+                                   Lang.bind(this, this._onCaptionTextKeyPressEvent));
+
+        this._captionLabel.clutterText.connect("text-changed",
+                                               Lang.bind(this,
+                                                         this._onCaptionTextChanged));
+
+        this._contentBox.append(this._captionLabel,
+                                Tb.BoxPackFlags.NONE);
+    },
+
+    _connectSoundSignals : function(fromState) {
+        this._disconnectSoundSignals();
+
+        this._soundEosId =
+            this._sound.connect("eos",
+                                Lang.bind(this,
+                                          this._onSoundEos));
+
+        this._soundErrorId =
+            this._sound.connect("error",
+                                Lang.bind(this,
+                                          this._onSoundError));
+
+        this._soundSizeChangeId =
+            this._sound.connect("size-change",
+                                Lang.bind(this,
+                                          this._onSoundSizeChange,
+                                          fromState));
+    },
+
+    _disconnectSoundSignals : function() {
+        if (this._soundEosId) {
+            this._sound.disconnect(this._soundEosId);
+            delete this._soundEosId
+        }
+
+        if (this._soundErrorId) {
+            this._sound.disconnect(this._soundErrorId);
+            delete this._soundErrorId;
+        }
+
+        if (this._soundSizeChangeId) {
+            this._sound.disconnect(this._soundSizeChangeId);
+            delete this._soundSizeChangeId
+        }
+    },
+
+    _updateSpinner : function() {
+        // FIXME: show/hide spinner depending on the
+        // loading state of the sound
+    },
+
+    _updateSoundFilename : function(soundFilename, fromState) {
+        if (this._soundFilename == soundFilename) {
+            return;
+        }
+
+        this._soundFilename = soundFilename;
+
+        if (this._soundFilename) {
+            this._connectSoundSignals(fromState);
+            this._updateSpinner();
+
+            // hide sound while loading the new sound file
+            this._sound.opacity = 0;
+
+            // start loading the new sound file
+            this._sound.set_filename(this._soundFilename);
+
+            this._sound.playing = true;
+            this._playButton.toggled = true;
+
+            if (fromState) {
+                let pauseSound = function() {
+                    this._sound.playing = false;
+                    this._playButton.toggled = false;
+                };
+
+                Mainloop.timeout_add(500,
+                                     Lang.bind(this, pauseSound));
+            }
+        }
+
+        if (!fromState) {
+            this.emit('save');
+        }
+    },
+
+    _updateInitialSize : function(width, height) {
+        let aspectRatio = width / height;
+
+        let newWidth;
+        let newHeight;
+
+        // FIXME: the sound might be actually smaller
+        // than the space available. What do we do in
+        // that case?
+
+        if (width >= height) {
+            newWidth = this._initialWidth;
+            newHeight = newWidth / aspectRatio;
+        } else {
+            newHeight = this._initialHeight;
+            newWidth = newHeight * aspectRatio;
+        }
+
+        this._soundWidth = newWidth;
+        this._soundHeight = newHeight;
+    },
+
+    _showSoundControlsBox : function() {
+        Tweener.addTween(this._soundControlsBox,
+                         { opacity: 255,
+                           time: _SHOW_BUTTON_BOX_TIME,
+                           onStart: function() {
+                               this.show();
+                           }});
+    },
+
+    _hideSoundControlsBox : function() {
+        Tweener.addTween(this._soundControlsBox,
+                         { opacity: 0,
+                           time: _SHOW_BUTTON_BOX_TIME,
+                           onComplete: function() {
+                               this.hide();
+                           }});
+    },
+
+    _updateSoundWithFileChooser : function() {
+        let chooser = new Gtk.FileChooserDialog();
+
+        chooser.add_button(Gtk.STOCK_CANCEL,
+                           Gtk.ResponseType.REJECT);
+        chooser.add_button(Gtk.STOCK_OK,
+                           Gtk.ResponseType.ACCEPT);
+
+        let soundFilter = new Gtk.FileFilter();
+
+        //soundFilter.set_name('Sounds');
+        //chooser.add_filter(soundFilter);
+
+        let soundsDir = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_VIDEOS);
+        chooser.set_current_folder(soundsDir);
+
+        chooser.set_transient_for(this.context.gtkWindow);
+
+        let response = chooser.run();
+
+        if (response != Gtk.ResponseType.ACCEPT) {
+            chooser.destroy();
+            return;
+        }
+
+        let filename = chooser.get_filename();
+
+        // Destroy dialog first, then set sound
+        chooser.destroy();
+
+        // we have to restore key focus on stage
+        // because the chooser grabs key focus
+        this._captionLabel.clutterText.grab_key_focus();
+
+        this._updateSoundFilename(filename,
+                                  false /* not from state*/);
+    },
+
+    _updateForSoundLoaded : function(fromState) {
+        this._disconnectSoundSignals();
+        this._updateSpinner();
+
+        [minTextHeight, naturalTextHeight] =
+            this._captionLabel.get_preferred_height(-1);
+
+        let thingWidth = this._soundWidth;
+        let thingHeight = this._soundHeight + naturalTextHeight;
+
+        this._minWidth = thingWidth;
+        this._minHeight = thingHeight;
+
+        if (!fromState) {
+            Tweener.addTween(this.actor,
+                             { width: thingWidth,
+                               height: thingHeight,
+                               time: fromState ? 0 : _UPDATE_SIZE_TIME,
+                               transition: _UPDATE_SIZE_TRANSITION });
+        }
+
+        Tweener.addTween(this._sound,
+                         { opacity: 255,
+                           delay: fromState ? 0 : _UPDATE_SIZE_TIME,
+                           time: fromState ? 0 : _UPDATE_SIZE_TIME });
+
+        delete this._soundWidth;
+        delete this._soundHeight;
+    },
+
+    _updateProgressBar : function() {
+        //this._progressBar.progress = this._sound.get_progress();
+    },
+
+    _formatTimeComponent : function(n) {
+        // FIXME: we need a sprinf equivalent to do
+        // proper formatting here.
+        return (n >= 10 ? n : "0" + n);
+    },
+
+    _updateTimeLabel : function() {
+        return;
+        let currentTime =
+            Math.floor(this._sound.duration * this._sound.get_progress());
+
+        let hours = Math.floor(currentTime / 3600);
+        currentTime -= hours * 3600;
+
+        let minutes = Math.floor(currentTime / 60);
+        currentTime -= minutes * 60;
+
+        let seconds = currentTime;
+
+        this._timeLabel.text = this._formatTimeComponent(hours) + ":" +
+                               this._formatTimeComponent(minutes) + ":" +
+                               this._formatTimeComponent(seconds);
+    },
+
+    _onCaptionTextKeyPressEvent : function(o, event) {
+        let key = event.get_key_symbol();
+
+        switch (key) {
+        case Clutter.Return:
+            this._updateSoundWithFileChooser();
+            return true;
+        case Clutter.Escape:
+            this.emit("deactivate");
+            return true;
+        }
+
+        return false;
+    },
+
+    _onCaptionTextChanged : function() {
+        this.emit('save');
+    },
+
+    _onPlayButtonToggled : function() {
+        this._sound.playing = this._playButton.toggled;
+    },
+
+    _onProgressBarButtonPressEvent : function(progressBar, event) {
+        let [eventX, eventY] = event.get_coords();
+
+        let [transformedX, transformedY] =
+            this._progressBar.get_transformed_position();
+
+        let progress = (eventX - transformedX) / this._progressBar.width;
+        this._sound.set_progress(progress);
+    },
+
+    _onSoundProgressChanged : function() {
+        this._updateProgressBar();
+        this._updateTimeLabel();
+    },
+
+    _onSoundEos : function(sound) {
+        print('VIDEO EOS');
+    },
+
+    _onSoundError : function(sound) {
+        print('VIDEO ERROR');
+    },
+
+    _onSoundSizeChange : function(sound, width, height, fromState) {
+        if (this._soundWidth > 0 && this._soundHeight > 0) {
+            return;
+        }
+
+        this._updateInitialSize(width, height);
+        this._updateForSoundLoaded(fromState);
+    },
+
+    enter : function() {
+        if (this._soundFilename) {
+            this._showSoundControlsBox();
+        }
+    },
+
+    leave : function() {
+        this._hideSoundControlsBox();
+    },
+
+    activate : function() {
+        this._captionLabel.clutterText.editable = true;
+        this._captionLabel.clutterText.grab_key_focus();
+    },
+
+    deactivate : function() {
+        this._captionLabel.clutterText.editable = false;
+    },
+
+    loadState : function(state) {
+        if ('soundFilename' in state) {
+            let fromState = 'width' in state &&
+                            'height' in state;
+
+            this._updateSoundFilename(state.soundFilename,
+                                      fromState);
+        }
+
+        if ('text' in state) {
+            this._captionLabel.text = state.text;
+        }
+    },
+
+    getState : function() {
+        return { soundFilename: this._soundFilename,
+                 text: this._captionLabel.text };
+    },
+
+    doAction : function(actionName, actionArgs) {
+        if (actionName == "chooseFile") {
+            this._updateSoundWithFileChooser();
+        }
+    },
+
+    validateSize : function(width, height) {
+        // minWidth and minHeight always have a valid aspect
+        // ratio once the sound is loaded (see _onSoundLoadFinished)
+        let aspectRatio = this._minWidth / this._minHeight;
+
+        // the point here is to keep aspect ratio while
+        // resize the sound thing
+        if (this._minWidth > this._minHeight) {
+            return [width, width / aspectRatio];
+        } else {
+            return [height * aspectRatio, height];
+        }
+    },
+
+    destroy : function() {
+        if (this._playButtonClickedId) {
+            this._playButton.disconnect(this._playButtonClickedId);
+            delete this._playButtonClickedId;
+        }
+
+        this._disconnectSoundSignals();
+
+        if (this._soundBox) {
+            this._soundBox.destroy();
+            delete this._soundBox;
+        }
+    },
+
+    get initialWidth() {
+        return _INITIAL_WIDTH;
+    },
+
+    get initialHeight() {
+        return _INITIAL_HEIGHT;
+    },
+
+    get minWidth() {
+        return _INITIAL_WIDTH;
+    },
+
+    get minHeight() {
+        return _INITIAL_HEIGHT;
+    },
+
+    get contentActor() {
+        return this._soundBox;
+    }
+}
+
+function create(args) {
+    return new SoundThing(args);
+}
+
+function createToolbar(args) {
+    let toolbar =
+        new Toolbar.Toolbar({ title: NAME,
+                              visible: false });
+
+    let toolBox =
+        new ToolBox.ToolBox({ title: Gettext.gettext("Load from"),
+                              isThingToolBox: true });
+
+    toolBox.addButton({ label: Gettext.gettext("File"),
+                        actionName: "chooseFile" });
+
+    toolbar.addToolBox(toolBox);
+
+    return toolbar;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]