28package com.jogamp.graph.ui.widgets;
30import java.util.Arrays;
32import java.util.Locale;
33import java.util.concurrent.atomic.AtomicReference;
35import com.jogamp.common.av.PTS;
36import com.jogamp.common.net.Uri;
37import com.jogamp.common.os.Clock;
38import com.jogamp.common.util.InterruptSource;
39import com.jogamp.graph.curve.Region;
40import com.jogamp.graph.curve.opengl.GLRegion;
41import com.jogamp.graph.font.Font;
42import com.jogamp.graph.font.FontFactory;
43import com.jogamp.graph.ui.Group;
44import com.jogamp.graph.ui.Scene;
45import com.jogamp.graph.ui.Shape;
46import com.jogamp.graph.ui.TooltipText;
47import com.jogamp.graph.ui.layout.Alignment;
48import com.jogamp.graph.ui.layout.BoxLayout;
49import com.jogamp.graph.ui.layout.Gap;
50import com.jogamp.graph.ui.layout.GridLayout;
51import com.jogamp.graph.ui.layout.Padding;
52import com.jogamp.graph.ui.shapes.Button;
53import com.jogamp.graph.ui.shapes.HUDShape;
54import com.jogamp.graph.ui.shapes.Label;
55import com.jogamp.graph.ui.shapes.MediaButton;
56import com.jogamp.graph.ui.shapes.Rectangle;
57import com.jogamp.math.FloatUtil;
58import com.jogamp.math.Vec2f;
59import com.jogamp.math.Vec3f;
60import com.jogamp.math.Vec4f;
61import com.jogamp.math.geom.AABBox;
62import com.jogamp.newt.event.MouseEvent;
63import com.jogamp.opengl.GL;
64import com.jogamp.opengl.GL2ES2;
65import com.jogamp.opengl.GLAnimatorControl;
66import com.jogamp.opengl.GLAutoDrawable;
67import com.jogamp.opengl.GLEventAdapter;
68import com.jogamp.opengl.GLProfile;
69import com.jogamp.opengl.util.av.GLMediaPlayer;
70import com.jogamp.opengl.util.av.GLMediaPlayerFactory;
71import com.jogamp.opengl.util.av.GLMediaPlayer.EventMask;
72import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException;
73import com.jogamp.opengl.util.texture.TextureSequence;
75import jogamp.graph.ui.TreeTool;
82 private static final boolean DEBUG =
false;
88 private static final float BorderSz = 0.01f;
89 private static final float BorderSzS = 0.03f;
90 private static final Vec4f BorderColor =
new Vec4f(0, 0, 0, 0.5f);
91 private static final Vec4f BorderColorA =
new Vec4f(0, 0, 0.5f, 0.5f);
92 private static final float AlphaBlend = 0.3f;
93 private static final float KnobScale = 2f;
94 private static final float StillPlayerScale = 1/3f;
95 private static final float ChapterTipScaleY = 5f;
96 private static final float ToolTipScaleY = 0.6f;
113 final Uri medium,
final float aratio,
final boolean letterBox,
final float zoomSize,
114 final boolean enableStills,
final List<Shape> customCtrls)
119 if(
null == fontInfo ||
null == fontSymbols ) {
124 final float superZOffset = zEpsilon * 20f;
126 final int ctrlCellsInt = 11+3;
127 final int ctrlCells = Math.max(customCtrls.size() + ctrlCellsInt, 20);
129 final float ctrlCellWidth = (aratio-2*BorderSzS)/ctrlCells;
130 final float ctrlCellHeight = ctrlCellWidth;
131 final float ctrlSliderHeightMin = ctrlCellHeight/6f;
132 final float ctrlSliderHeightMax = KnobScale * ctrlSliderHeightMin;
134 final AtomicReference<Shape> zoomReplacement =
new AtomicReference<Shape>();
135 final AtomicReference<Vec3f> zoomOrigScale =
new AtomicReference<Vec3f>();
136 final AtomicReference<Vec3f> zoomOrigPos =
new AtomicReference<Vec3f>();
142 mButton =
new MediaButton(renderModes, aratio, 1, mPlayer);
150 final float knobScale = ctrlSliderHeightMax / ctrlSliderHeightMin;
151 ctrlSlider =
new RangeSlider(renderModes,
new Vec2f(aratio - ctrlSliderHeightMax, ctrlSliderHeightMin), knobScale,
new Vec2f(0, 100), 1000, 0);
152 final float dx = ctrlSliderHeightMax / 2f;
153 final float dy = ( ctrlSliderHeightMax - ctrlSliderHeightMin ) * 0.5f;
158 ctrlSlider.
move(0, 0, zEpsilon);
160 ctrlSlider.
setName(
"mp.slider");
165 final Runnable reshapeStillHUD;
168 final float labelW = aratio/4f;
169 final float labelH = 1f/10f;
170 stillTime =
new Button(renderModes, fontInfo, PTS.toTimeStr(0), labelW, labelH, 0);
176 stillTime.
moveTo(aratio/2f-labelW/2f, 0, 0);
190 stillMedia =
new MediaButton(renderModes, aratio, 1.0f, stillPlayer);
194 stillMedia.
moveTo(0, labelH*1.2f, 0);
201 enableStills ? aratio*StillPlayerScale : labelW*StillPlayerScale,
202 enableStills ? 1f*StillPlayerScale : labelH*StillPlayerScale,
203 renderModes, ctrlSlider, stillGroup);
206 reshapeStillHUD = () -> {
208 final float labelW2 = ar/4f;
209 final float labelH2 = 1f/10f;
211 stillMedia.
moveTo(0, labelH2*1.2f, 0);
212 stillTime.
moveTo(ar/2f-labelW2/2f, 0, 0);
213 stillHUD.
setClientSize(ar*StillPlayerScale, 1f*StillPlayerScale);
217 final Button playButton =
new Button(renderModes, fontSymbols,
228 final Button cropButton =
new Button(renderModes, fontSymbols,
234 System.err.println(
"MediaButton AttributesChanges: "+eventMask+
", when "+when);
235 System.err.println(
"MediaButton State: "+mp);
238 audioLabel.setText(
"audio\n"+mp.getLang(mp.getAID()));
239 subLabel.setText(
"sub\n"+mp.getLang(mp.getSID()));
240 ctrlSlider.setMinMax(new Vec2f(0, mp.getDuration()), 0);
241 System.err.println(
"Init "+mp.toString());
243 for(final GLMediaPlayer.Chapter c : mp.getChapters()) {
245 System.err.println(c);
247 final Shape mark = ctrlSlider.addMark(c.start, new Vec4f(0.9f, 0.9f, 0.9f, 0.5f));
248 mark.setToolTip(new TooltipText(c.title+
"\n@ "+PTS.toTimeStr(c.start, false)+
", duration "+PTS.toTimeStr(c.duration(), false), fontInfo, ChapterTipScaleY));
250 final float aratioVideo = (
float)mPlayer.
getWidth() / (
float)mPlayer.
getHeight();
252 cropButton.setVisible(false);
253 System.err.println(
"AR Crop disabled: aratioPlayer "+aratio+
", aratioVideo "+aratioVideo+
": "+mPlayer.getTitle());
255 System.err.println(
"AR Crop enabled: aratioPlayer "+aratio+
", aratioVideo "+aratioVideo+
": "+mPlayer.getTitle());
258 scene.invoke(false, (final GLAutoDrawable d) -> {
260 stillPlayer.playStream(mPlayer.getUri(), mPlayer.getVID(), GLMediaPlayer.STREAM_ID_NONE, GLMediaPlayer.STREAM_ID_NONE, 1);
261 reshapeStillHUD.run();
266 playButton.setToggle(
true);
267 }
else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Pause) ) {
268 playButton.setToggle(
false);
270 if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.EOS) ) {
271 final StreamException err = mp.getStreamException();
273 System.err.println(
"MovieSimple State: Exception: "+err.getMessage());
275 new InterruptSource.Thread() {
286 this.addShape(mButton);
288 Group ctrlGroup, infoGroup;
290 final Label muteLabel, infoLabel;
291 final Button timeLabel;
292 final float infoGroupHeight;
293 final boolean[] hud_sticky = {
false };
294 final boolean[] info_full = {
false };
296 muteLabel =
new Label(renderModes, fontSymbols, aratio/6f, fontSymbols.getUTF16String(
"music_off"));
297 muteLabel.setName(
"mp.mute");
299 final float sz = aratio/6f;
300 muteLabel.setColor(1, 0, 0, 1);
301 muteLabel.setPaddding(
new Padding(0, 0, 1f-sz, aratio-sz));
303 muteLabel.setInteractive(
false);
304 muteLabel.setVisible( mPlayer.isAudioMuted() );
305 this.addShape(muteLabel);
308 infoGroup =
new Group(
new BoxLayout());
309 infoGroup.setName(
"mp.info").setInteractive(
false);
310 this.addShape( infoGroup.setVisible(
false) );
312 final String text =
"88:88 / 88:88:88 (88 %), playing (8.88x, vol 8.88), A/R 8.88, vid 8 (H264), aid 8 (eng, AC3), sid 8 (eng, HDMV_PGS)\n"+
313 "JogAmp's GraphUI Full-Feature Media Player Widget Rocks!";
314 final AABBox textBounds = fontInfo.getGlyphBounds(text);
315 final float lineHeight = textBounds.getHeight()/2f;
316 final float sxy = aratio/(1.1f*textBounds.getWidth());
317 infoLabel =
new Label(renderModes, fontInfo, text);
318 infoLabel.setName(
"mp.info.label");
319 infoLabel.setInteractive(
false);
320 infoLabel.setColor(1, 1, 1, 0.9f);
321 infoLabel.scale(sxy, sxy, 1f);
323 final float dy = 0.5f*lineHeight*sxy;
324 infoGroupHeight = 2.5f*dy + textBounds.getHeight()*sxy;
326 System.err.println(
"XXX: sxy "+sxy+
", b "+textBounds);
327 System.err.println(
"XXX: GroupHeight "+infoGroupHeight+
", dy "+dy+
", lineHeight*sxy "+(lineHeight*sxy));
328 System.err.println(
"XXX: b.getHeight() * sxy "+(textBounds.getHeight() * sxy));
329 System.err.println(
"XXX: (1f-GroupHeight+dy)/sxy "+((1f-infoGroupHeight+dy)/sxy));
331 infoLabel.setPaddding(
new Padding(0, 0, (1f-infoGroupHeight+dy)/sxy, 0.5f));
333 final Rectangle rect =
new Rectangle(renderModes & ~Region.AA_RENDERING_MASK, aratio, infoGroupHeight, 0);
334 rect.setName(
"mp.info.blend").setInteractive(
false);
335 rect.setColor(0, 0, 0, AlphaBlend);
336 rect.setPaddding(
new Padding(0, 0, 1f-infoGroupHeight, 0));
338 infoGroup.addShape(rect);
339 infoGroup.addShape(infoLabel);
342 timeLabel =
new Button(renderModes, fontInfo,
343 getMultilineTime(Clock.currentMillis(), mPlayer), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
344 timeLabel.setName(
"mp.time");
345 timeLabel.setPerp().setColor(CtrlCellCol);
346 timeLabel.setLabelColor(1, 1, 1, 1.0f);
348 scene.addGLEventListener(
new GLEventAdapter() {
351 public void display(
final GLAutoDrawable drawable) {
352 final GLAnimatorControl anim = drawable.getAnimator();
353 if( ( timeLabel.isVisible() || infoLabel.isVisible() ) &&
354 ( mPlayer.getState() == GLMediaPlayer.State.Playing ||
355 mPlayer.getState() == GLMediaPlayer.State.Paused ) &&
358 final long t1 = anim.getTotalFPSDuration();
359 if( t1 - t0 >= 333) {
361 final int ptsMS = mPlayer.getPTS().getCurrent();
362 final int durationMS = mPlayer.getDuration();
363 infoLabel.setText(getInfo(ptsMS, durationMS, mPlayer, info_full[0]));
364 timeLabel.setText(getMultilineTime(ptsMS, durationMS));
365 ctrlSlider.setValue(ptsMS);
370 ctrlSlider.addChangeListener((
final RangeSlider w,
final float old_val,
final float val,
final float old_val_pct,
final float val_pct,
final Vec3f pos,
final MouseEvent e) -> {
372 System.err.println(
"Dragged "+w.getName()+
": "+PTS.toTimeStr(Math.round(val),
true)+
"ms, "+(val_pct*100f)+
"%");
373 System.err.println(
"Slider.D "+ctrlSlider.getDescription());
375 final int dir_val = (int)Math.signum(val - old_val);
376 final int currentPTS = mPlayer.getPTS().getCurrent();
377 final int nextPTS = Math.round( val );
378 final int dir_pts = (int)Math.signum(nextPTS - currentPTS);
379 if( dir_val == dir_pts ) {
380 scene.invoke(
false, (
final GLAutoDrawable d) -> {
381 final int durationMS = mPlayer.getDuration();
382 timeLabel.setText(getMultilineTime(nextPTS, durationMS));
383 mPlayer.seek(nextPTS);
389 final int[] lastPeekPTS = { 0 };
390 ctrlSlider.addPeekListener((
final RangeSlider w,
final float val,
final float val_pct,
final Vec3f pos,
final MouseEvent e) -> {
391 final float res = Math.max(1000, w.getRange() / 300f);
392 final int nextPTS = Math.round( val/1000f ) * 1000;
393 final Vec3f pos2 =
new Vec3f(pos.x()-stillHUD.getClientSize().x()/2f, ctrlSliderHeightMax, pos.z() + ctrlSlider.getPosition().z());
394 stillHUD.moveToHUDPos(pos2);
396 stillTime.setText(PTS.toTimeStr(nextPTS,
false));
397 stillHUD.setVisible(
true);
398 if( enableStills && Math.abs(lastPeekPTS[0] - nextPTS ) >= res ) {
399 scene.invoke(
false, (
final GLAutoDrawable d) -> {
400 stillPlayer.seek(nextPTS);
403 lastPeekPTS[0] = nextPTS;
406 ctrlSlider.addActivationListener((
final Shape s) -> {
409 stillTime.setText(PTS.toTimeStr(mPlayer.getPTS().getCurrent(),
false));
410 stillHUD.setVisible(
true);
412 stillHUD.setVisible(
false);
416 ctrlBlend =
new Rectangle(renderModes & ~Region.AA_RENDERING_MASK, aratio, ctrlCellHeight, 0);
417 ctrlBlend.setName(
"ctrl.blend").setInteractive(
false);
418 ctrlBlend.setColor(0, 0, 0, AlphaBlend);
419 this.addShape( ctrlBlend.setVisible(
false) );
421 this.addShape( ctrlSlider.setVisible(
false) );
423 ctrlGroup =
new Group(
new GridLayout(ctrlCellWidth, ctrlCellHeight, Alignment.FillCenter, Gap.None, 1));
424 ctrlGroup.setName(
"ctrlGroup").setInteractive(
false);
425 ctrlGroup.setPaddding(
new Padding(0, BorderSzS, 0, BorderSzS));
426 this.addShape( ctrlGroup.setVisible(
false) );
428 playButton.onToggle((
final Shape s) -> {
429 if( s.isToggleOn() ) {
430 mPlayer.setPlaySpeed(1);
433 mPlayer.pause(false);
436 playButton.setToggle(
true);
437 ctrlGroup.addShape(playButton);
438 playButton.setToolTip(
new TooltipText(
"Play/Pause", fontInfo, ToolTipScaleY));
441 final Button button =
new Button(renderModes, fontSymbols,
442 fontSymbols.getUTF16String(
"skip_previous"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
443 button.setName(
"back");
444 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
445 button.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
446 final int pts = mPlayer.getPTS().getCurrent();
448 final GLMediaPlayer.Chapter c0 = mPlayer.getChapter(mPlayer.getPTS().getCurrent());
450 if( pts > c0.start + 5000 ) {
453 final GLMediaPlayer.Chapter c1 = mPlayer.getChapter(c0.start - 1);
461 mPlayer.seek(targetMS);
463 ctrlGroup.addShape(button);
464 button.setToolTip(
new TooltipText(
"Prev Chapter", fontInfo, ToolTipScaleY));
467 final Button button =
new Button(renderModes, fontSymbols,
468 fontSymbols.getUTF16String(
"skip_next"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
469 button.setName(
"next");
470 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
471 button.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
473 final GLMediaPlayer.Chapter c0 = mPlayer.getChapter(mPlayer.getPTS().getCurrent());
475 final GLMediaPlayer.Chapter c1 = mPlayer.getChapter(c0.end + 1);
481 }
else if( mPlayer.getChapters().length > 0 ) {
484 targetMS = mPlayer.getPTS().getCurrent() * ( 1 + 1 / ( 60000 * 10 ) );
486 mPlayer.seek(targetMS);
488 ctrlGroup.addShape(button);
489 button.setToolTip(
new TooltipText(
"Next Chapter", fontInfo, ToolTipScaleY));
492 final Button button =
new Button(renderModes, fontSymbols,
493 fontSymbols.getUTF16String(
"fast_rewind"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
494 button.setName(
"rv-");
495 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
496 button.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
497 final float v = mPlayer.getPlaySpeed();
499 mPlayer.setPlaySpeed(v / 2.0f);
501 mPlayer.setPlaySpeed(v - 0.5f);
504 ctrlGroup.addShape(button);
505 button.setToolTip(
new TooltipText(
"replay speed: v <= 1 ? v/2 : v-0.5", fontInfo, ToolTipScaleY));
508 final Button button =
new Button(renderModes, fontSymbols,
509 fontSymbols.getUTF16String(
"fast_forward"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
510 button.setName(
"rv+");
511 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
512 button.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
513 final float v = mPlayer.getPlaySpeed();
514 if( 1f > v && v + 0.5f > 1f ) {
515 mPlayer.setPlaySpeed(1);
517 mPlayer.setPlaySpeed(v + 0.5f);
520 ctrlGroup.addShape(button);
521 button.setToolTip(
new TooltipText(
"replay speed: v+0.5", fontInfo, ToolTipScaleY));
524 final Button button =
new Button(renderModes, fontSymbols,
525 fontSymbols.getUTF16String(
"replay_10"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
526 button.setName(
"rew5");
527 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
528 button.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
529 mPlayer.seek(mPlayer.getPTS().getCurrent() - 10000);
531 button.addMouseListener(
new Shape.MouseGestureAdapter() {
533 public void mouseWheelMoved(final MouseEvent e) {
534 final int pts0 = mPlayer.getPTS().getCurrent();
535 final int pts1 = pts0 + (int)(e.getRotation()[1]*10000f);
537 System.err.println(
"Seek: "+pts0+
" -> "+pts1);
541 ctrlGroup.addShape(button);
542 button.setToolTip(
new TooltipText(
"Replay 10s (+scroll)", fontInfo, ToolTipScaleY));
545 final Button button =
new Button(renderModes, fontSymbols,
546 fontSymbols.getUTF16String(
"forward_10"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
547 button.setName(
"fwd5");
548 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
549 button.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
550 mPlayer.seek(mPlayer.getPTS().getCurrent() + 10000);
552 button.addMouseListener(
new Shape.MouseGestureAdapter() {
554 public void mouseWheelMoved(final MouseEvent e) {
555 final int pts0 = mPlayer.getPTS().getCurrent();
556 final int pts1 = pts0 + (int)(e.getRotation()[1]*10000f);
558 System.err.println(
"Seek: "+pts0+
" -> "+pts1);
562 ctrlGroup.addShape(button);
563 button.setToolTip(
new TooltipText(
"Forward 10s (+scroll)", fontInfo, ToolTipScaleY));
566 final Button button =
new Button(renderModes, fontSymbols,
567 fontSymbols.getUTF16String(
"volume_up"), fontSymbols.getUTF16String(
"volume_mute"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
568 button.setName(
"mute");
569 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
570 final float[] volume = { 1.0f };
571 button.onToggle( (
final Shape s) -> {
572 if( s.isToggleOn() ) {
573 mPlayer.setAudioVolume( volume[0] );
575 mPlayer.setAudioVolume( 0f );
577 muteLabel.setVisible( !s.isToggleOn() );
579 button.addMouseListener(
new Shape.MouseGestureAdapter() {
581 public void mouseWheelMoved(final MouseEvent e) {
582 mPlayer.setAudioVolume( mPlayer.getAudioVolume() + e.getRotation()[1]/20f );
583 volume[0] = mPlayer.getAudioVolume();
585 button.setToggle( !mPlayer.isAudioMuted() );
586 ctrlGroup.addShape(button);
587 button.setToolTip(
new TooltipText(
"Volume (+scroll)", fontInfo, ToolTipScaleY));
590 audioLabel.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
591 final int next_aid = mPlayer.getNextAID();
592 if( mPlayer.getAID() != next_aid ) {
593 mPlayer.switchStream(mPlayer.getVID(), next_aid, mPlayer.getSID());
596 ctrlGroup.addShape(audioLabel);
597 audioLabel.setToolTip(
new TooltipText(
"Audio Language", fontInfo, ToolTipScaleY));
600 subLabel.onClicked((
final Shape s,
final Vec3f pos,
final MouseEvent e) -> {
601 final int next_sid = mPlayer.getNextSID();
602 if( mPlayer.getSID() != next_sid ) {
603 mPlayer.switchStream(mPlayer.getVID(), mPlayer.getAID(), next_sid);
606 ctrlGroup.addShape(subLabel);
607 subLabel.setToolTip(
new TooltipText(
"Subtitle Language", fontInfo, ToolTipScaleY));
610 ctrlGroup.addShape(timeLabel);
612 for(
int i=11; i<ctrlCells-3-customCtrls.size(); ++i) {
613 final Button button =
new Button(renderModes, fontInfo,
" ", CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
614 button.setName(
"ctrl_"+i);
615 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
616 ctrlGroup.addShape(button);
619 final Button button =
new Button(renderModes, fontSymbols,
620 fontSymbols.getUTF16String(
"visibility"), fontSymbols.getUTF16String(
"visibility_off"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
621 button.setName(
"hud");
622 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
623 button.onToggle( (
final Shape s) -> {
624 hud_sticky[0] = s.isToggleOn();
626 button.setToggle(
false );
627 ctrlGroup.addShape(button);
628 button.setToolTip(
new TooltipText(
"Sticky HUD", fontInfo, ToolTipScaleY));
631 final boolean[] value = { !letterBox };
632 cropButton.onToggle( (
final Shape s) -> {
633 value[0] = !value[0];
636 ctrlGroup.addShape(cropButton);
637 cropButton.setToolTip(
new TooltipText(
"Letterbox Crop", fontInfo, ToolTipScaleY));
640 final Button button =
new Button(renderModes, fontSymbols,
641 fontSymbols.getUTF16String(
"zoom_out_map"), fontSymbols.getUTF16String(
"zoom_in_map"), CtrlButtonWidth, CtrlButtonHeight, zEpsilon);
642 button.setName(
"zoom");
643 button.setSpacing(SymSpacing, FixedSymSize).setPerp().setColor(CtrlCellCol);
644 final boolean fullScene = FloatUtil.isEqual(1f, zoomSize);
645 final boolean wasDraggable = isDraggable();
646 button.onToggle( (
final Shape s) -> {
647 if( s.isToggleOn() ) {
650 MediaPlayer.this.setBorder(0f);
651 MediaPlayer.this.setDraggable(false);
653 final AABBox sbox = scene.getBounds();
654 final Group parent =
this.getParent();
655 final float sx = sbox.getWidth() * zoomSize /
this.getScaledWidth();
656 final float sy = sbox.getHeight() * zoomSize /
this.getScaledHeight();
657 final float sxy = Math.min(sx, sy);
658 Shape _zoomReplacement =
null;
659 zoomOrigScale.set(
this.getScale().copy() );
660 zoomOrigPos.set(
this.getPosition().copy() );
661 if(
null != parent ) {
663 _zoomReplacement = new Label(renderModes, fontInfo, aratio/40f,
"zoomed");
664 final boolean r = parent.replaceShape(this, _zoomReplacement);
668 this.scale(sxy, sxy, 1f);
669 this.moveTo(sbox.getLow()).move(sbox.getWidth() * ( 1f - zoomSize )/2.0f, sbox.getHeight() * ( 1f - zoomSize )/2.0f, superZOffset);
670 scene.addShape(this);
677 this.scale(sxy, sxy, 1f);
678 this.moveTo(sbox.getLow()).move(sbox.getWidth() * ( 1f - zoomSize )/2.0f, sbox.getHeight() * ( 1f - zoomSize )/2.0f, superZOffset);
681 zoomReplacement.set( _zoomReplacement );
685 MediaPlayer.this.setBorder(BorderSz);
686 MediaPlayer.this.setDraggable(wasDraggable);
688 final Vec3f _zoomOrigScale = zoomOrigScale.getAndSet(
null);
689 final Vec3f _zoomOrigPos = zoomOrigPos.getAndSet(
null);
690 final Shape _zoomReplacement = zoomReplacement.getAndSet(
null);
691 if(
null != _zoomReplacement ) {
692 final Group parent = _zoomReplacement.getParent();
693 scene.removeShape(
this);
694 if(
null != _zoomOrigScale ) {
695 this.setScale(_zoomOrigScale);
697 if(
null != _zoomOrigPos ) {
698 this.moveTo(_zoomOrigPos);
700 parent.replaceShape(_zoomReplacement,
this);
703 if(
null != _zoomOrigScale ) {
704 this.setScale(_zoomOrigScale);
706 if(
null != _zoomOrigPos ) {
707 this.moveTo(_zoomOrigPos);
711 if(
null != _zoomReplacement ) {
712 scene.invoke(
true, (drawable) -> {
713 final GL2ES2 gl = drawable.getGL().getGL2ES2();
714 _zoomReplacement .destroy(gl, scene.getRenderer());
720 button.setToggle(
false );
721 ctrlGroup.addShape(button);
722 button.setToolTip(
new TooltipText(
"Zoom", fontInfo, ToolTipScaleY));
724 for(
final Shape cs : customCtrls ) {
725 ctrlGroup.addShape(cs);
728 this.enableTopLevelWidget(scene);
730 this.addActivationListener( (
final Shape s) -> {
731 if( this.isActive() ) {
732 this.setBorderColor(BorderColorA);
734 final boolean hud_on = hud_sticky[0];
735 this.setBorderColor(BorderColor);
736 ctrlSlider.setVisible(hud_on);
737 ctrlBlend.setVisible(hud_on);
738 ctrlGroup.setVisible(hud_on);
739 infoGroup.setVisible(hud_on);
742 this.addMouseListener(
new Shape.MouseGestureAdapter() {
744 public void mouseClicked(final MouseEvent e) {
745 final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
746 final Vec3f p = shapeEvent.objPos;
747 if( p.y() > ( 1f - infoGroupHeight ) ) {
748 info_full[0] = !info_full[0];
749 final float sxy = infoLabel.getScale().x();
750 final float p_bottom_s = infoLabel.getPadding().bottom * sxy;
757 infoLabel.setScale(sxy2, sxy2, 1f);
758 infoLabel.setPaddding(new Padding(0, 0, p_bottom_s/sxy2, 0.5f));
762 public void mouseMoved(
final MouseEvent e) {
763 final Shape.EventInfo shapeEvent = (Shape.EventInfo) e.getAttachment();
764 final Vec3f p = shapeEvent.objPos;
765 final boolean c = hud_sticky[0] || ( ctrlCellHeight + ctrlSliderHeightMax ) > p.y() || p.y() > ( 1f - infoGroupHeight );
766 ctrlSlider.setVisible(c);
767 ctrlBlend.setVisible(c);
768 ctrlGroup.setVisible(c);
769 infoGroup.setVisible(c);
772 public void mouseReleased(
final MouseEvent e) {
776 public void mouseDragged(
final MouseEvent e) {
780 TreeTool.forAll(
this, (
final Shape s) -> { s.setDraggable(
false).setResizable(
false);
return false; });
781 ctrlSlider.getKnob().setDraggable(
true);
786 if(
null != mButton ) {
800 if(
null != mButton ) {
810 if(
null != mButton ) {
816 return getInfo(mPlayer.
getPTS().get(currentMillis), mPlayer.
getDuration(), mPlayer, full);
818 public static String
getInfo(
final int ptsMS,
final int durationMS,
final GLMediaPlayer mPlayer,
final boolean full) {
819 final String chapter;
821 final GLMediaPlayer.Chapter c = mPlayer.
getChapter(ptsMS);
823 chapter =
" - "+c.
title;
829 final float pct = (float)ptsMS / (
float)durationMS;
831 final String text1 = String.format(
"%s / %s (%.0f %%), %s (%01.2fx, vol %1.2f), A/R %.2f, fps %02.1f, kbps %.2f",
832 PTS.toTimeStr(ptsMS,
false), PTS.toTimeStr(durationMS,
false), pct*100,
835 final String text2 = String.format(
"video: id %d (%s), kbps %.2f, codec %s/'%s'",
837 final String text3 = String.format(
"audio: id %d/%s (%s/%s), kbps %.2f, codec %s/'%s'",
841 final String text4 = String.format(
"sub : id %d/%s (%s/%s), codec %s/'%s'",
845 return text1+
"\n"+text2+
"\n"+text3+
"\n"+text4+
"\n"+mPlayer.
getTitle()+chapter;
847 final String vinfo, ainfo, sinfo;
848 if( mPlayer.
getVID() != GLMediaPlayer.STREAM_ID_NONE ) {
853 if( mPlayer.
getAID() != GLMediaPlayer.STREAM_ID_NONE ) {
858 if( mPlayer.
getSID() != GLMediaPlayer.STREAM_ID_NONE ) {
863 final String text1 = String.format(
"%s / %s (%.0f %%), %s (%01.2fx, vol %1.2f), A/R %.2f%s%s%s",
864 PTS.toTimeStr(ptsMS,
false), PTS.toTimeStr(durationMS,
false), pct*100,
866 return text1+
"\n"+mPlayer.
getTitle()+chapter;
870 return getMultilineTime(mPlayer.
getPTS().get(currentMillis), mPlayer.
getDuration());
873 final float pct = (float)ptsMS / (
float)durationMS;
874 return String.format(
"%.0f %%%n%s%n%s",
875 pct*100, PTS.toTimeStr(ptsMS,
false), PTS.toTimeStr(durationMS,
false));
The optional property jogamp.graph.font.ctor allows user to specify the FontConstructor implementatio...
static Font getDefaultFont()
Returns default Font of default FontSet or null if n/a.
static Font getSymbolsFont()
Returns the default symbols Font or null if n/a.
Group of Shapes, optionally utilizing a Group.Layout.
Group()
Create a group of Shapes w/o Group.Layout.
void addShape(final Shape s)
Adds a Shape.
void addShape(final Shape s)
Adds a Shape.
static float getZEpsilon(final int zBits, final PMVMatrixSetup setup)
Default Z precision on 16-bit depth buffer using -1 z-position and DEFAULT_ZNEAR.
Shape setColor(final float r, final float g, final float b, final float a)
Set base color.
Shape setName(final String name)
Set a symbolic name for this shape for identification.
final Shape move(final float dtx, final float dty, final float dtz)
Move about scaled distance.
final Shape setInteractive(final boolean v)
Set whether this shape is interactive in general, i.e.
final Shape setFixedARatioResize(final boolean v)
Sets whether aspect-ratio shall be kept at resize, if isResizable().
final Shape moveTo(final float tx, final float ty, final float tz)
Move to scaled position.
Shape setPressedColorMod(final float r, final float g, final float b, final float a)
Set pressed color, modulating getColor() if isPressed().
final Shape setPaddding(final Padding padding)
Sets the unscaled padding for this shape, which is included in unscaled getBounds() and also includes...
final Shape setVisible(final boolean v)
Enable (default) or disable this shape's visibility.
final Shape setBorderColor(final float r, final float g, final float b, final float a)
Set border color.
final Shape setBorder(final float thickness)
Sets the thickness of the border, which is included in getBounds() and is outside of getPadding().
final Shape setToggleable(final boolean toggleable)
Set this shape toggleable, default is off.
Immutable layout alignment options, including Bit#Fill.
static final Alignment None
No alignment constant.
GraphUI Stack Group.Layout.
GraphUI CSS property Padding, unscaled space belonging to the element and included in the element's s...
A Head Up Display (HUD) Shape for a client Shape using inner size and Mv position to be displayed on ...
HUDShape setClientSize(final float clientWidth, final float clientHeight)
Sets the client Shape size of this HUD in given targetShape object Mv-space, not Scene.
Basic Float math utility functions.
static boolean isZero(final float a, final float epsilon)
Returns true if value is zero, i.e.
2D Vector based upon two float components.
4D Vector based upon four float components.
Interface wrapper for font implementation.
static final int GL_LINEAR
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_LINEAR" with expressi...