[the-board/sound-thing: 3/4] sound thing stuff
- From: Lucas Rocha <lucasr src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [the-board/sound-thing: 3/4] sound thing stuff
- Date: Wed, 5 Jan 2011 10:30:30 +0000 (UTC)
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]