bubble notifications (close #111)
This commit is contained in:
parent
18d579eb62
commit
c6464ffb25
|
@ -38,6 +38,7 @@ import yugecin.opsudance.core.errorhandling.ErrorDumpable;
|
||||||
import yugecin.opsudance.core.inject.InstanceContainer;
|
import yugecin.opsudance.core.inject.InstanceContainer;
|
||||||
import yugecin.opsudance.core.state.OpsuState;
|
import yugecin.opsudance.core.state.OpsuState;
|
||||||
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
|
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
|
||||||
|
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState;
|
||||||
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
|
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
|
||||||
import yugecin.opsudance.core.state.transitions.*;
|
import yugecin.opsudance.core.state.transitions.*;
|
||||||
import yugecin.opsudance.events.ResolutionChangedEvent;
|
import yugecin.opsudance.events.ResolutionChangedEvent;
|
||||||
|
@ -59,6 +60,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
|
||||||
|
|
||||||
private FpsRenderState fpsState;
|
private FpsRenderState fpsState;
|
||||||
private BarNotificationState barNotifState;
|
private BarNotificationState barNotifState;
|
||||||
|
private BubbleNotificationState bubNotifState;
|
||||||
|
|
||||||
private TransitionState outTransitionState;
|
private TransitionState outTransitionState;
|
||||||
private TransitionState inTransitionState;
|
private TransitionState inTransitionState;
|
||||||
|
@ -76,6 +78,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
|
||||||
public int width;
|
public int width;
|
||||||
public int height;
|
public int height;
|
||||||
|
|
||||||
|
public int mouseX;
|
||||||
|
public int mouseY;
|
||||||
|
|
||||||
public int targetRenderInterval;
|
public int targetRenderInterval;
|
||||||
public int targetBackgroundRenderInterval;
|
public int targetBackgroundRenderInterval;
|
||||||
|
|
||||||
|
@ -126,6 +131,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
|
||||||
|
|
||||||
fpsState = instanceContainer.provide(FpsRenderState.class);
|
fpsState = instanceContainer.provide(FpsRenderState.class);
|
||||||
barNotifState = instanceContainer.provide(BarNotificationState.class);
|
barNotifState = instanceContainer.provide(BarNotificationState.class);
|
||||||
|
bubNotifState = instanceContainer.provide(BubbleNotificationState.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,6 +142,8 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
|
||||||
timeSinceLastRender += delta;
|
timeSinceLastRender += delta;
|
||||||
|
|
||||||
input.poll(width, height);
|
input.poll(width, height);
|
||||||
|
mouseX = input.getMouseX();
|
||||||
|
mouseY = input.getMouseY();
|
||||||
state.update(delta);
|
state.update(delta);
|
||||||
|
|
||||||
int maxRenderInterval;
|
int maxRenderInterval;
|
||||||
|
@ -159,6 +167,7 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
|
||||||
state.render(graphics);
|
state.render(graphics);
|
||||||
fpsState.render(graphics);
|
fpsState.render(graphics);
|
||||||
barNotifState.render(graphics, timeSinceLastRender);
|
barNotifState.render(graphics, timeSinceLastRender);
|
||||||
|
bubNotifState.render(graphics, timeSinceLastRender);
|
||||||
|
|
||||||
realRenderInterval = timeSinceLastRender;
|
realRenderInterval = timeSinceLastRender;
|
||||||
timeSinceLastRender = 0;
|
timeSinceLastRender = 0;
|
||||||
|
@ -319,6 +328,9 @@ public class DisplayContainer implements ErrorDumpable, KeyListener, MouseListen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseReleased(int button, int x, int y) {
|
public void mouseReleased(int button, int x, int y) {
|
||||||
|
if (bubNotifState.mouseReleased(x, y)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
state.mouseReleased(button, x, y);
|
state.mouseReleased(button, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import yugecin.opsudance.PreStartupInitializer;
|
||||||
import yugecin.opsudance.core.DisplayContainer;
|
import yugecin.opsudance.core.DisplayContainer;
|
||||||
import yugecin.opsudance.core.events.EventBus;
|
import yugecin.opsudance.core.events.EventBus;
|
||||||
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
|
import yugecin.opsudance.core.state.specialstates.BarNotificationState;
|
||||||
|
import yugecin.opsudance.core.state.specialstates.BubbleNotificationState;
|
||||||
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
|
import yugecin.opsudance.core.state.specialstates.FpsRenderState;
|
||||||
import yugecin.opsudance.core.state.transitions.EmptyTransitionState;
|
import yugecin.opsudance.core.state.transitions.EmptyTransitionState;
|
||||||
import yugecin.opsudance.core.state.transitions.FadeInTransitionState;
|
import yugecin.opsudance.core.state.transitions.FadeInTransitionState;
|
||||||
|
@ -41,6 +42,7 @@ public class OpsuDanceInjector extends Injector {
|
||||||
|
|
||||||
bind(FpsRenderState.class).asEagerSingleton();
|
bind(FpsRenderState.class).asEagerSingleton();
|
||||||
bind(BarNotificationState.class).asEagerSingleton();
|
bind(BarNotificationState.class).asEagerSingleton();
|
||||||
|
bind(BubbleNotificationState.class).asEagerSingleton();
|
||||||
|
|
||||||
bind(EmptyTransitionState.class).asEagerSingleton();
|
bind(EmptyTransitionState.class).asEagerSingleton();
|
||||||
bind(FadeInTransitionState.class).asEagerSingleton();
|
bind(FadeInTransitionState.class).asEagerSingleton();
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* opsu!dance - fork of opsu! with cursordance auto
|
||||||
|
* Copyright (C) 2017 yugecin
|
||||||
|
*
|
||||||
|
* opsu!dance is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* opsu!dance 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package yugecin.opsudance.core.state.specialstates;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.ui.Fonts;
|
||||||
|
import itdelatrisu.opsu.ui.animations.AnimationEquation;
|
||||||
|
import org.newdawn.slick.Color;
|
||||||
|
import org.newdawn.slick.Graphics;
|
||||||
|
import yugecin.opsudance.core.DisplayContainer;
|
||||||
|
import yugecin.opsudance.core.events.EventBus;
|
||||||
|
import yugecin.opsudance.core.events.EventListener;
|
||||||
|
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||||
|
import yugecin.opsudance.events.ResolutionChangedEvent;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
public class BubbleNotificationState implements EventListener<BubbleNotificationEvent> {
|
||||||
|
|
||||||
|
public static final int IN_TIME = 633;
|
||||||
|
public static final int DISPLAY_TIME = 7000 + IN_TIME;
|
||||||
|
public static final int OUT_TIME = 433;
|
||||||
|
public static final int TOTAL_TIME = DISPLAY_TIME + OUT_TIME;
|
||||||
|
|
||||||
|
private final DisplayContainer displayContainer;
|
||||||
|
private final LinkedList<Notification> bubbles;
|
||||||
|
|
||||||
|
private int addAnimationTime;
|
||||||
|
private int addAnimationHeight;
|
||||||
|
|
||||||
|
public BubbleNotificationState(DisplayContainer displayContainer, EventBus eventBus) {
|
||||||
|
this.displayContainer = displayContainer;
|
||||||
|
this.bubbles = new LinkedList<>();
|
||||||
|
this.addAnimationTime = IN_TIME;
|
||||||
|
eventBus.subscribe(BubbleNotificationEvent.class, this);
|
||||||
|
eventBus.subscribe(ResolutionChangedEvent.class, new EventListener<ResolutionChangedEvent>() {
|
||||||
|
@Override
|
||||||
|
public void onEvent(ResolutionChangedEvent event) {
|
||||||
|
calculatePositions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(Graphics g, int delta) {
|
||||||
|
ListIterator<Notification> iter = bubbles.listIterator();
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addAnimationTime += delta;
|
||||||
|
if (addAnimationTime > IN_TIME) {
|
||||||
|
finishAddAnimation();
|
||||||
|
}
|
||||||
|
boolean animateUp = false;
|
||||||
|
do {
|
||||||
|
Notification next = iter.next();
|
||||||
|
if (animateUp && addAnimationTime < IN_TIME) {
|
||||||
|
next.y = next.baseY - (int) (addAnimationHeight * AnimationEquation.OUT_QUINT.calc((float) addAnimationTime / IN_TIME));
|
||||||
|
}
|
||||||
|
if (next.render(g, displayContainer.mouseX, displayContainer.mouseY, delta)) {
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
|
animateUp = true;
|
||||||
|
} while (iter.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mouseReleased(int x, int y) {
|
||||||
|
if (x < displayContainer.width - Notification.width) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Notification bubble : bubbles) {
|
||||||
|
if (bubble.mouseReleased(x, y)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculatePositions() {
|
||||||
|
Notification.width = (int) (displayContainer.width * 0.1703125f);
|
||||||
|
Notification.baseLine = (int) (displayContainer.height * 0.9645f);
|
||||||
|
Notification.paddingY = (int) (displayContainer.height * 0.0144f);
|
||||||
|
Notification.finalX = displayContainer.width - Notification.width - (int) (displayContainer.width * 0.01);
|
||||||
|
Notification.fontPaddingX = (int) (Notification.width * 0.02f);
|
||||||
|
Notification.fontPaddingY = (int) (Fonts.SMALLBOLD.getLineHeight() / 4f);
|
||||||
|
Notification.lineHeight = Fonts.SMALLBOLD.getLineHeight();
|
||||||
|
if (bubbles.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finishAddAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishAddAnimation() {
|
||||||
|
if (bubbles.isEmpty()) {
|
||||||
|
addAnimationHeight = 0;
|
||||||
|
addAnimationTime = IN_TIME;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ListIterator<Notification> iter = bubbles.listIterator();
|
||||||
|
iter.next();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Notification bubble = iter.next();
|
||||||
|
bubble.y = bubble.baseY - addAnimationHeight;
|
||||||
|
bubble.baseY = bubble.y;
|
||||||
|
}
|
||||||
|
addAnimationHeight = 0;
|
||||||
|
addAnimationTime = IN_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(BubbleNotificationEvent event) {
|
||||||
|
finishAddAnimation();
|
||||||
|
Notification newBubble = new Notification(event.message, event.borderColor);
|
||||||
|
bubbles.add(0, newBubble);
|
||||||
|
addAnimationTime = 0;
|
||||||
|
addAnimationHeight = newBubble.height + Notification.paddingY;
|
||||||
|
ListIterator<Notification> iter = bubbles.listIterator();
|
||||||
|
iter.next();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Notification next = iter.next();
|
||||||
|
next.baseY = next.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Notification {
|
||||||
|
|
||||||
|
private final static int HOVER_ANIM_TIME = 150;
|
||||||
|
|
||||||
|
private static int width;
|
||||||
|
private static int finalX;
|
||||||
|
private static int baseLine;
|
||||||
|
private static int fontPaddingX;
|
||||||
|
private static int fontPaddingY;
|
||||||
|
private static int lineHeight;
|
||||||
|
private static int paddingY;
|
||||||
|
|
||||||
|
private final Color bgcol;
|
||||||
|
private final Color textColor;
|
||||||
|
private final Color borderColor;
|
||||||
|
private final Color targetBorderColor;
|
||||||
|
|
||||||
|
private int timeShown;
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
private int baseY;
|
||||||
|
private int height;
|
||||||
|
private List<String> lines;
|
||||||
|
private boolean isFading;
|
||||||
|
|
||||||
|
private int hoverTime;
|
||||||
|
|
||||||
|
private Notification(String message, Color borderColor) {
|
||||||
|
this.lines = Fonts.wrap(Fonts.SMALLBOLD, message, (int) (width * 0.96f), true);
|
||||||
|
this.height = (int) (Fonts.SMALLBOLD.getLineHeight() * (lines.size() + 0.5f));
|
||||||
|
this.targetBorderColor = borderColor;
|
||||||
|
this.borderColor = new Color(borderColor);
|
||||||
|
this.textColor = new Color(Color.white);
|
||||||
|
this.bgcol = new Color(Color.black);
|
||||||
|
this.y = baseLine - height;
|
||||||
|
this.baseY = this.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean render(Graphics g, int mouseX, int mouseY, int delta) {
|
||||||
|
timeShown += delta;
|
||||||
|
processAnimations(isMouseHovered(mouseX, mouseY), delta);
|
||||||
|
g.setColor(bgcol);
|
||||||
|
g.fillRoundRect(x, y, width, height, 6);
|
||||||
|
g.setLineWidth(2f);
|
||||||
|
g.setColor(borderColor);
|
||||||
|
g.drawRoundRect(x, y, width, height, 6);
|
||||||
|
int y = this.y + fontPaddingY;
|
||||||
|
for (String line : lines) {
|
||||||
|
Fonts.SMALLBOLD.drawString(x + fontPaddingX, y, line, textColor);
|
||||||
|
y += lineHeight;
|
||||||
|
}
|
||||||
|
return timeShown > BubbleNotificationState.TOTAL_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processAnimations(boolean mouseHovered, int delta) {
|
||||||
|
if (mouseHovered) {
|
||||||
|
hoverTime = Math.min(HOVER_ANIM_TIME, hoverTime + delta);
|
||||||
|
} else {
|
||||||
|
hoverTime = Math.max(0, hoverTime - delta);
|
||||||
|
}
|
||||||
|
float hoverProgress = (float) hoverTime / HOVER_ANIM_TIME;
|
||||||
|
borderColor.r = targetBorderColor.r + (0.977f - targetBorderColor.r) * hoverProgress;
|
||||||
|
borderColor.g = targetBorderColor.g + (0.977f - targetBorderColor.g) * hoverProgress;
|
||||||
|
borderColor.b = targetBorderColor.b + (0.977f - targetBorderColor.b) * hoverProgress;
|
||||||
|
if (timeShown < BubbleNotificationState.IN_TIME) {
|
||||||
|
float progress = (float) timeShown / BubbleNotificationState.IN_TIME;
|
||||||
|
this.x = finalX + (int) ((1 - AnimationEquation.OUT_BACK.calc(progress)) * width / 2);
|
||||||
|
textColor.a = borderColor.a = bgcol.a = progress;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x = Notification.finalX;
|
||||||
|
if (timeShown > BubbleNotificationState.DISPLAY_TIME) {
|
||||||
|
isFading = true;
|
||||||
|
float progress = (float) (timeShown - BubbleNotificationState.DISPLAY_TIME) / BubbleNotificationState.OUT_TIME;
|
||||||
|
textColor.a = borderColor.a = bgcol.a = 1f - progress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean mouseReleased(int x, int y) {
|
||||||
|
if (!isFading && isMouseHovered(x, y)) {
|
||||||
|
timeShown = BubbleNotificationState.DISPLAY_TIME;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMouseHovered(int x, int y) {
|
||||||
|
return this.x <= x && x < this.x + width && this.y <= y && y <= this.y + this.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/yugecin/opsudance/events/BubbleNotificationEvent.java
Normal file
37
src/yugecin/opsudance/events/BubbleNotificationEvent.java
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* opsu!dance - fork of opsu! with cursordance auto
|
||||||
|
* Copyright (C) 2017 yugecin
|
||||||
|
*
|
||||||
|
* opsu!dance is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* opsu!dance 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with opsu!dance. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package yugecin.opsudance.events;
|
||||||
|
|
||||||
|
import org.newdawn.slick.Color;
|
||||||
|
|
||||||
|
public class BubbleNotificationEvent {
|
||||||
|
|
||||||
|
public static final Color COMMONCOLOR_RED = new Color(138, 72, 51);
|
||||||
|
public static final Color COMMONCOLOR_GREEN = new Color(98, 131, 59);
|
||||||
|
public static final Color COMMONCOLOR_WHITE = new Color(220, 220, 220);
|
||||||
|
public static final Color COMMONCOLOR_PURPLE = new Color(94, 46, 149);
|
||||||
|
|
||||||
|
public final String message;
|
||||||
|
public final Color borderColor;
|
||||||
|
|
||||||
|
public BubbleNotificationEvent(String message, Color borderColor) {
|
||||||
|
this.message = message;
|
||||||
|
this.borderColor = borderColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ import org.newdawn.slick.Graphics;
|
||||||
import yugecin.opsudance.core.DisplayContainer;
|
import yugecin.opsudance.core.DisplayContainer;
|
||||||
import yugecin.opsudance.core.state.OpsuState;
|
import yugecin.opsudance.core.state.OpsuState;
|
||||||
import yugecin.opsudance.events.BarNotificationEvent;
|
import yugecin.opsudance.events.BarNotificationEvent;
|
||||||
|
import yugecin.opsudance.events.BubbleNotificationEvent;
|
||||||
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ public class EmptyRedState implements OpsuState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean keyPressed(int key, char c) {
|
public boolean keyPressed(int key, char c) {
|
||||||
System.out.println("pressed");
|
displayContainer.eventBus.post(new BubbleNotificationEvent("this is a bubble notification... bubbly bubbly bubbly linewraaaaaaaaaap", BubbleNotificationEvent.COMMONCOLOR_RED));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +85,7 @@ public class EmptyRedState implements OpsuState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean mouseWheelMoved(int delta) {
|
public boolean mouseWheelMoved(int delta) {
|
||||||
|
displayContainer.eventBus.post(new BubbleNotificationEvent("Life is like a box of chocolates. It's all going to melt by the end of the day.\n-Emily", BubbleNotificationEvent.COMMONCOLOR_PURPLE));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user