mirror of
https://github.com/cyberarm/cyberarm_engine.git
synced 2025-12-16 05:02:34 +00:00
242 lines
7.2 KiB
Ruby
242 lines
7.2 KiB
Ruby
module CyberarmEngine
|
|
class NotificationManager
|
|
EDGE_TOP = :top
|
|
EDGE_BOTTOM = :bottom
|
|
EDGE_RIGHT = :right
|
|
EDGE_LEFT = :left
|
|
|
|
MODE_DEFAULT = :slide
|
|
MODE_CIRCLE = :circle
|
|
|
|
attr_reader :edge, :mode, :max_visible, :notifications
|
|
def initialize(edge: EDGE_RIGHT, mode: MODE_DEFAULT, window:, max_visible: 1)
|
|
@edge = edge
|
|
@mode = mode
|
|
@window = window
|
|
@max_visible = max_visible
|
|
|
|
@notifications = []
|
|
@drivers = []
|
|
@slots = Array.new(max_visible, nil)
|
|
end
|
|
|
|
def draw
|
|
@drivers.each do |driver|
|
|
case @edge
|
|
when :left, :right
|
|
x = @edge == :right ? @window.width + driver.x : -Notification::WIDTH + driver.x
|
|
y = driver.y + Notification::HEIGHT / 2
|
|
|
|
Gosu.translate(x, y + (Notification::HEIGHT + Notification::PADDING) * driver.slot) do
|
|
driver.draw
|
|
end
|
|
|
|
when :top, :bottom
|
|
x = @window.width / 2 - Notification::WIDTH / 2
|
|
y = @edge == :top ? driver.y - Notification::HEIGHT : @window.height + driver.y
|
|
slot_position = (Notification::HEIGHT + Notification::PADDING) * driver.slot
|
|
slot_position *= -1 if @edge == :bottom
|
|
|
|
Gosu.translate(x, y + slot_position) do
|
|
driver.draw
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def update
|
|
show_next_notification if @drivers.size < @max_visible
|
|
@drivers.each do |driver|
|
|
if driver.done?
|
|
@slots[driver.slot] = nil
|
|
@drivers.delete(driver)
|
|
end
|
|
end
|
|
|
|
@drivers.each(&:update)
|
|
end
|
|
|
|
def show_next_notification
|
|
notification = @notifications.sort { |n| n.priority }.reverse.shift
|
|
return unless notification
|
|
return if available_slot_index < lowest_used_slot
|
|
@notifications.delete(notification)
|
|
|
|
@drivers << Driver.new(edge: @edge, mode: @mode, notification: notification, slot: available_slot_index)
|
|
slot = @slots[available_slot_index] = @drivers.last
|
|
end
|
|
|
|
def available_slot_index
|
|
@slots.each_with_index do |slot, i|
|
|
return i unless slot
|
|
end
|
|
|
|
return -1
|
|
end
|
|
|
|
def lowest_used_slot
|
|
@slots.each_with_index do |slot, i|
|
|
return i if slot
|
|
end
|
|
|
|
return -1
|
|
end
|
|
|
|
def highest_used_slot
|
|
_slot = -1
|
|
@slots.each_with_index do |slot, i|
|
|
_slot = i if slot
|
|
end
|
|
|
|
return _slot
|
|
end
|
|
|
|
def create_notification(**args)
|
|
notification = Notification.new(host: self, **args)
|
|
@notifications << notification
|
|
end
|
|
|
|
class Driver
|
|
attr_reader :x, :y, :notification, :slot
|
|
def initialize(edge:, mode:, notification:, slot:)
|
|
@edge = edge
|
|
@mode = mode
|
|
@notification = notification
|
|
@slot = slot
|
|
|
|
@x, @y = 0, 0
|
|
@delta = Gosu.milliseconds
|
|
@accumulator = 0.0
|
|
|
|
@born_at = Gosu.milliseconds
|
|
@duration_completed_at = Float::INFINITY
|
|
@transition_completed_at = Float::INFINITY
|
|
end
|
|
|
|
def transition_in_complete?
|
|
Gosu.milliseconds - @born_at >= @notification.transition_duration
|
|
end
|
|
|
|
def duration_completed?
|
|
Gosu.milliseconds - @transition_completed_at >= @notification.time_to_live
|
|
end
|
|
|
|
def done?
|
|
Gosu.milliseconds - @duration_completed_at >= @notification.transition_duration
|
|
end
|
|
|
|
def draw
|
|
ratio = 0.0
|
|
|
|
if not transition_in_complete?
|
|
ratio = animation_ratio
|
|
elsif transition_in_complete? and not duration_completed?
|
|
ratio = 1.0
|
|
elsif duration_completed?
|
|
ratio = 1.0 - animation_ratio
|
|
end
|
|
|
|
case @mode
|
|
when MODE_DEFAULT
|
|
Gosu.clip_to(0, 0, Notification::WIDTH, Notification::HEIGHT * ratio) do
|
|
@notification.draw
|
|
end
|
|
when MODE_CIRCLE
|
|
half = Notification::WIDTH / 2
|
|
|
|
Gosu.clip_to(half - (half * ratio), 0, Notification::WIDTH * ratio, Notification::HEIGHT) do
|
|
@notification.draw
|
|
end
|
|
end
|
|
end
|
|
|
|
def update
|
|
case @mode
|
|
when MODE_DEFAULT
|
|
update_default
|
|
when MODE_CIRCLE
|
|
update_circle
|
|
end
|
|
|
|
@accumulator += Gosu.milliseconds - @delta
|
|
@delta = Gosu.milliseconds
|
|
end
|
|
|
|
|
|
def update_default
|
|
case @edge
|
|
when :left, :right
|
|
if not transition_in_complete? # Slide In
|
|
@x = @edge == :right ? -x_offset : x_offset
|
|
elsif transition_in_complete? and not duration_completed?
|
|
@x = @edge == :right ? -Notification::WIDTH : Notification::WIDTH if @x.abs != Notification::WIDTH
|
|
@transition_completed_at = Gosu.milliseconds if @transition_completed_at == Float::INFINITY
|
|
@accumulator = 0.0
|
|
elsif duration_completed? # Slide Out
|
|
@x = @edge == :right ? x_offset - Notification::WIDTH : Notification::WIDTH - x_offset
|
|
@x = 0 if @edge == :left and @x <= 0
|
|
@x = 0 if @edge == :right and @x >= 0
|
|
@duration_completed_at = Gosu.milliseconds if @duration_completed_at == Float::INFINITY
|
|
end
|
|
|
|
when :top, :bottom
|
|
if not transition_in_complete? # Slide In
|
|
@y = @edge == :top ? y_offset : -y_offset
|
|
elsif transition_in_complete? and not duration_completed?
|
|
@y = @edge == :top ? Notification::HEIGHT : -Notification::HEIGHT if @x.abs != Notification::HEIGHT
|
|
@transition_completed_at = Gosu.milliseconds if @transition_completed_at == Float::INFINITY
|
|
@accumulator = 0.0
|
|
elsif duration_completed? # Slide Out
|
|
@y = @edge == :top ? Notification::HEIGHT - y_offset : y_offset - Notification::HEIGHT
|
|
@y = 0 if @edge == :top and @y <= 0
|
|
@y = 0 if @edge == :bottom and @y >= 0
|
|
@duration_completed_at = Gosu.milliseconds if @duration_completed_at == Float::INFINITY
|
|
end
|
|
end
|
|
end
|
|
|
|
def update_circle
|
|
case @edge
|
|
when :top, :bottom
|
|
@y = @edge == :top ? Notification::HEIGHT : -Notification::HEIGHT
|
|
when :left, :right
|
|
@x = @edge == :right ? -Notification::WIDTH : Notification::WIDTH
|
|
end
|
|
|
|
if transition_in_complete? and not duration_completed?
|
|
@transition_completed_at = Gosu.milliseconds if @transition_completed_at == Float::INFINITY
|
|
@accumulator = 0.0
|
|
elsif duration_completed?
|
|
@duration_completed_at = Gosu.milliseconds if @duration_completed_at == Float::INFINITY
|
|
end
|
|
end
|
|
|
|
def animation_ratio
|
|
x = (@accumulator / @notification.transition_duration)
|
|
|
|
case @notification.transition_type
|
|
when Notification::LINEAR_TRANSITION
|
|
x.clamp(0.0, 1.0)
|
|
when Notification::EASE_IN_OUT_TRANSITION # https://easings.net/#easeInOutQuint
|
|
(x < 0.5 ? 16 * x * x * x * x * x : 1 - ((-2 * x + 2) ** 5) / 2).clamp(0.0, 1.0)
|
|
end
|
|
end
|
|
|
|
def x_offset
|
|
if not transition_in_complete? or duration_completed?
|
|
Notification::WIDTH * animation_ratio
|
|
else
|
|
0
|
|
end
|
|
end
|
|
|
|
def y_offset
|
|
if not transition_in_complete? or duration_completed?
|
|
Notification::HEIGHT * animation_ratio
|
|
else
|
|
0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end |