mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-07 05:57:47 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
--Dialog: 3D dialogs for ROBLOX in VR
|
||||
--written by 0xBAADF00D
|
||||
--6/30/2016
|
||||
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local InputService = game:GetService("UserInputService")
|
||||
local Utility = require(RobloxGui.Modules.Settings.Utility)
|
||||
local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
|
||||
|
||||
local DIALOG_BG_COLOR = Color3.new(0.2, 0.2, 0.2)
|
||||
local DIALOG_BG_TRANSPARENCY = 0.3
|
||||
local DIALOG_TITLE_HEIGHT = 66
|
||||
local DIALOG_COLOR_HEIGHT = 8
|
||||
local DIALOG_TITLE_TEXT_SIZE = Enum.FontSize.Size36
|
||||
local DIALOG_CONTENT_PADDING = 48
|
||||
local TITLE_COLOR = Color3.new(1, 1, 1)
|
||||
|
||||
local PANEL_OFFSET_CF = CFrame.new(0, 0, -7) * CFrame.Angles(0, math.pi, 0)
|
||||
|
||||
local emptySelectionImage = Utility:Create "ImageLabel" {
|
||||
Name = "EmptySelectionImage",
|
||||
Image = "",
|
||||
BackgroundTransparency = 1,
|
||||
ImageTransparency = 1
|
||||
}
|
||||
|
||||
local DialogPanel = Panel3D.Get("Dialog")
|
||||
DialogPanel:SetType(Panel3D.Type.Fixed)
|
||||
DialogPanel.localCF = PANEL_OFFSET_CF
|
||||
DialogPanel:SetCanFade(true)
|
||||
|
||||
local dialogPanelAngle = 0
|
||||
local opacityLookup = {}
|
||||
local resetBaseAngle = false
|
||||
local baseAngle = 0
|
||||
|
||||
|
||||
local PANEL_FADE_ANGLE_0, PANEL_FADE_ANGLE_1 = math.rad(37.5), math.rad(44)
|
||||
local PANEL_FADE_RANGE = PANEL_FADE_ANGLE_1 - PANEL_FADE_ANGLE_0
|
||||
local PANEL_REAPPEAR_ANGLE = math.rad(90)
|
||||
|
||||
local function positionDialogPanel(desiredAngle)
|
||||
dialogPanelAngle = desiredAngle
|
||||
local headCF = InputService:GetUserCFrame(Enum.UserCFrame.Head)
|
||||
local headPos = headCF.p
|
||||
DialogPanel.localCF = CFrame.new(headPos) * CFrame.Angles(0, desiredAngle, 0) * PANEL_OFFSET_CF
|
||||
end
|
||||
|
||||
function DialogPanel:CalculateTransparency()
|
||||
local headCF = InputService:GetUserCFrame(Enum.UserCFrame.Head)
|
||||
local headLook = headCF.lookVector * Vector3.new(1, 0, 1)
|
||||
local vertAngle = math.asin(headCF.lookVector.Y)
|
||||
local vectorToPanel = Vector3.new(math.cos(baseAngle + dialogPanelAngle + math.rad(90)), 0, -math.sin(baseAngle + dialogPanelAngle + math.rad(90)))
|
||||
|
||||
if math.abs(vertAngle) > PANEL_FADE_ANGLE_1 then
|
||||
resetBaseAngle = true
|
||||
end
|
||||
|
||||
local angleToPanel = math.acos(headLook:Dot(vectorToPanel))
|
||||
|
||||
return math.min(math.max(0, (angleToPanel - PANEL_FADE_ANGLE_0) / PANEL_FADE_RANGE), 1)
|
||||
end
|
||||
|
||||
|
||||
local function updatePanelPosition()
|
||||
local headCF = InputService:GetUserCFrame(Enum.UserCFrame.Head)
|
||||
local headLook = headCF.lookVector * Vector3.new(1, 0, 1)
|
||||
local headAngle = (math.atan2(-headLook.Z, headLook.X) - math.rad(90)) % math.rad(360)
|
||||
local newPanelAngle = baseAngle + math.floor((headAngle / PANEL_REAPPEAR_ANGLE) + 0.5) * PANEL_REAPPEAR_ANGLE
|
||||
|
||||
if resetBaseAngle then
|
||||
resetBaseAngle = false
|
||||
baseAngle = headAngle
|
||||
end
|
||||
|
||||
positionDialogPanel(newPanelAngle)
|
||||
end
|
||||
|
||||
|
||||
game:GetService("RunService"):BindToRenderStep("DialogPanel", Enum.RenderPriority.First.Value, function()
|
||||
if DialogPanel.transparency == 1 or resetBaseAngle then
|
||||
updatePanelPosition()
|
||||
end
|
||||
|
||||
--update the transparency of gui elements
|
||||
local opacityMult = 1 - DialogPanel.transparency
|
||||
for guiElement, baseOpacity in pairs(opacityLookup) do
|
||||
local transparency = 1 - (baseOpacity * opacityMult)
|
||||
if guiElement:IsA("TextLabel") or guiElement:IsA("TextButton") then
|
||||
guiElement.TextTransparency = transparency
|
||||
elseif guiElement:IsA("ImageLabel") or guiElement:IsA("ImageButton") then
|
||||
guiElement.ImageTransparency = transparency
|
||||
elseif guiElement:IsA("Frame") then
|
||||
guiElement.BackgroundTransparency = transparency
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local DialogQueue = {}
|
||||
|
||||
local currentDescendantConn = nil
|
||||
local lastDialogShown = nil
|
||||
local function updatePanel()
|
||||
local currentDialog = DialogQueue[1]
|
||||
if lastDialogShown and lastDialogShown.content then
|
||||
lastDialogShown.content.Parent = nil
|
||||
end
|
||||
if not currentDialog or not currentDialog.content then
|
||||
DialogPanel:SetVisible(false)
|
||||
opacityLookup = {}
|
||||
if currentDescendantConn then
|
||||
currentDescendantConn:disconnect()
|
||||
currentDescendantConn = nil
|
||||
end
|
||||
else
|
||||
currentDialog.content.Parent = DialogPanel:GetGUI()
|
||||
lastDialogShown = currentDialog
|
||||
|
||||
local contentSize = currentDialog.content.AbsoluteSize
|
||||
DialogPanel:ResizePixels(contentSize.X, contentSize.Y, 100)
|
||||
|
||||
resetBaseAngle = true
|
||||
updatePanelPosition()
|
||||
DialogPanel:SetVisible(true)
|
||||
|
||||
opacityLookup = {}
|
||||
local function search(parent)
|
||||
if parent:IsA("ImageLabel") or parent:IsA("ImageButton") then
|
||||
opacityLookup[parent] = 1 - parent.ImageTransparency
|
||||
elseif parent:IsA("TextLabel") or parent:IsA("TextButton") then
|
||||
opacityLookup[parent] = 1 - parent.TextTransparency
|
||||
elseif parent:IsA("Frame") then
|
||||
opacityLookup[parent] = 1 - parent.BackgroundTransparency
|
||||
end
|
||||
for i, v in pairs(parent:GetChildren()) do
|
||||
search(v)
|
||||
end
|
||||
end
|
||||
search(DialogPanel:GetGUI())
|
||||
if currentDescendantConn then
|
||||
currentDescendantConn:disconnect()
|
||||
currentDescendantConn = nil
|
||||
end
|
||||
currentDescendantConn = DialogPanel:GetGUI().DescendantAdded:connect(function(descendant)
|
||||
search(descendant)
|
||||
end)
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Dialog = {}
|
||||
Dialog.__index = Dialog
|
||||
function Dialog.new(content)
|
||||
local self = setmetatable({}, Dialog)
|
||||
self.content = content
|
||||
return self
|
||||
end
|
||||
|
||||
function Dialog:SetContent(guiElement)
|
||||
if not guiElement and self.content then
|
||||
self.content.Parent = nil
|
||||
end
|
||||
self.content = guiElement
|
||||
end
|
||||
|
||||
function Dialog:Show(shouldTakeover)
|
||||
if shouldTakeover then
|
||||
table.insert(DialogQueue, 1, self)
|
||||
else
|
||||
table.insert(DialogQueue, self)
|
||||
end
|
||||
updatePanel()
|
||||
end
|
||||
|
||||
function Dialog:Close()
|
||||
for idx, dialog in pairs(DialogQueue) do
|
||||
if dialog == self then
|
||||
table.remove(DialogQueue, idx)
|
||||
break
|
||||
end
|
||||
end
|
||||
updatePanel()
|
||||
end
|
||||
|
||||
return Dialog
|
||||
@@ -0,0 +1,743 @@
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local Util = require(RobloxGui.Modules.Settings.Utility)
|
||||
local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
|
||||
local VRHub = require(RobloxGui.Modules.VR.VRHub)
|
||||
|
||||
local PANEL_OFFSET_CFRAME = CFrame.Angles(math.rad(-5), 0, 0) * CFrame.new(0, 4, 0) * CFrame.Angles(math.rad(-15), 0, 0)
|
||||
|
||||
local NO_TRANSITION_ANIMATIONS = false
|
||||
local ANIMATE_OUT_DISTANCE = -100
|
||||
local ANIMATE_OUT_DURATION = 0.25
|
||||
local PIXELS_PER_STUD = 150
|
||||
local WINDOW_TITLEBAR_HEIGHT = 72
|
||||
local BLURRED_TITLEBAR_COLOR = Color3.new(78 / 255, 84 / 255, 96 / 255)
|
||||
local FOCUSED_TITLEBAR_COLOR = Color3.new(82 / 255, 101 / 255, 141 / 255)
|
||||
local WINDOW_BG_COLOR = Color3.new(20/255, 20/255, 20/255)
|
||||
local WINDOW_BG_TRANSPARENCY = 0.5
|
||||
local POPOUT_DISTANCE = 0.25
|
||||
local POPOUT_DURATION = 0.25
|
||||
|
||||
local NOTIFICATION_WIDTH_SCALE = 0.85
|
||||
local NOTIFICATION_HEIGHT_OFFSET = 80
|
||||
local NOTIFICATION_PADDING_Y = 20
|
||||
local NOTIFICATION_PADDING_X_SCALE = (1 - NOTIFICATION_WIDTH_SCALE) / 2
|
||||
local NOTIFICATION_DEPTH_OFFSET = 0.25
|
||||
local NOTIFICATION_BG_COLOR = Color3.new(0.2, 0.2, 0.2)
|
||||
local NOTIFICATION_BG_TRANSPARENCY = 0.1
|
||||
local MAX_NOTIFICATIONS_SHOWN = 3
|
||||
local MAX_DETAILS_SHOWN = 2
|
||||
local DETAILS_PADDING = 20
|
||||
|
||||
local BUTTON_1_POS = 0.07
|
||||
local BUTTON_2_POS = 0.511
|
||||
local BUTTON_SINGLE_SIZE = 0.86
|
||||
local BUTTON_DOUBLE_SIZE = 0.415
|
||||
|
||||
local BUTTON_Y_POS = 0.55
|
||||
local BUTTON_Y_SIZE = 0.29
|
||||
|
||||
local BUTTON_NORMAL_IMG = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButton.png"
|
||||
local BUTTON_SELECTED_IMG = "rbxasset://textures/ui/Settings/MenuBarAssets/MenuButtonSelected.png"
|
||||
|
||||
local emptySelectionImage = Util:Create "ImageLabel" {
|
||||
BackgroundTransparency = 1,
|
||||
Image = ""
|
||||
}
|
||||
|
||||
|
||||
local AVATAR_IMAGE_URL = 'http://www.watrbx.wtf/thumbs/avatar.ashx?userId=%d&x=%d&y=%d'
|
||||
|
||||
local TEXT_COLOR = Color3.new(1, 1, 1)
|
||||
|
||||
local aspectRatio = 1.62666666
|
||||
local totalHeight = 3.5
|
||||
local totalWidth = totalHeight * aspectRatio
|
||||
local leftPanelWidth = totalWidth * 0.4
|
||||
local rightPanelWidth = totalWidth * 0.6
|
||||
|
||||
local panelOffset = totalWidth / 2
|
||||
local leftOffset = panelOffset - (leftPanelWidth * 0.5)
|
||||
local rightOffset = leftOffset - (leftPanelWidth * 0.5) - (rightPanelWidth * 0.5)
|
||||
|
||||
local NotificationHubModule = {}
|
||||
NotificationHubModule.ModuleName = "Notifications"
|
||||
NotificationHubModule.KeepVRTopbarOpen = true
|
||||
NotificationHubModule.VRIsExclusive = true
|
||||
NotificationHubModule.VRClosesNonExclusive = true
|
||||
NotificationHubModule.UnreadCountChanged = function() end
|
||||
VRHub:RegisterModule(NotificationHubModule)
|
||||
|
||||
local notificationsPanel = Panel3D.Get("Notifications")
|
||||
local notificationsWindow = nil
|
||||
local detailsPanel = Panel3D.Get("NotificationDetails")
|
||||
local detailsWindow = nil
|
||||
|
||||
local WindowFrame = {}
|
||||
do
|
||||
local windows = {}
|
||||
local WindowFrame_mt = { __index = WindowFrame }
|
||||
function WindowFrame.new(panel, parent, title)
|
||||
local instance = {}
|
||||
table.insert(windows, instance)
|
||||
instance.zeroCF = panel.localCF
|
||||
instance.zOffset = 0
|
||||
instance.isPopping = false
|
||||
instance.isAnimating = false
|
||||
instance.tweener = nil
|
||||
instance.panel = panel
|
||||
instance.panel.OnMouseEnter = function()
|
||||
for i, v in pairs(windows) do
|
||||
if v ~= instance then
|
||||
v:SetPopOut(false)
|
||||
end
|
||||
end
|
||||
instance:SetPopOut(true)
|
||||
end
|
||||
|
||||
instance.titlebar = Util:Create "ImageLabel" {
|
||||
Parent = parent,
|
||||
|
||||
Position = UDim2.new(0, -1, 0, -1),
|
||||
Size = UDim2.new(1, 2, 0, WINDOW_TITLEBAR_HEIGHT + 2),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = "rbxasset://textures/ui/VR/rectBackgroundWhite.png",
|
||||
ImageColor3 = BLURRED_TITLEBAR_COLOR,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(10, 10, 10, 10)
|
||||
}
|
||||
instance.titleText = Util:Create "TextLabel" {
|
||||
Parent = instance.titlebar,
|
||||
|
||||
Position = UDim2.new(0, 1, 0, 1),
|
||||
Size = UDim2.new(1, -2, 1, -2),
|
||||
|
||||
Text = title,
|
||||
TextColor3 = TEXT_COLOR,
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size36,
|
||||
|
||||
BackgroundTransparency = 1
|
||||
}
|
||||
|
||||
instance.content = Util:Create "ImageLabel" {
|
||||
Parent = parent,
|
||||
|
||||
Position = UDim2.new(0, -1, 0, WINDOW_TITLEBAR_HEIGHT + 2),
|
||||
Size = UDim2.new(1, 2, 1, -WINDOW_TITLEBAR_HEIGHT - 4),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = "rbxasset://textures/ui/VR/rectBackgroundWhite.png",
|
||||
ImageColor3 = WINDOW_BG_COLOR,
|
||||
ImageTransparency = WINDOW_BG_TRANSPARENCY,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(10, 10, 10, 10)
|
||||
}
|
||||
|
||||
return setmetatable(instance, WindowFrame_mt)
|
||||
end
|
||||
|
||||
function WindowFrame:SetTitle(title)
|
||||
self.titleText.Text = title
|
||||
end
|
||||
|
||||
function WindowFrame:AddCloseButton(callback)
|
||||
local closeBtnSize = 48
|
||||
local closeBtnOffset = 14
|
||||
self.closeButton = Util:Create "ImageButton" {
|
||||
Parent = self.titlebar,
|
||||
|
||||
Position = UDim2.new(0, closeBtnOffset, 0, closeBtnOffset),
|
||||
Size = UDim2.new(0, closeBtnSize, 0, closeBtnSize),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = "rbxasset://textures/ui/VR/closeButtonPadded.png"
|
||||
}
|
||||
self.closeButton.MouseButton1Click:connect(callback)
|
||||
end
|
||||
|
||||
function WindowFrame:TweenZOffsetTo(zOffset, duration, easingFunc, callback)
|
||||
if self.tweener and not self.tweener:IsFinished() then
|
||||
self.tweener:Cancel()
|
||||
end
|
||||
self.tweener = Util:TweenProperty(self, "zOffset", self.zOffset, zOffset, duration, easingFunc, callback)
|
||||
end
|
||||
|
||||
function WindowFrame:AnimateOut(callback)
|
||||
self.isAnimating = true
|
||||
self:TweenZOffsetTo(ANIMATE_OUT_DISTANCE, ANIMATE_OUT_DURATION, Util:GetEaseInOutQuad(), function()
|
||||
if callback then callback() end
|
||||
self.isAnimating = false
|
||||
end)
|
||||
end
|
||||
|
||||
function WindowFrame:AnimateIn(callback)
|
||||
self.zOffset = ANIMATE_OUT_DISTANCE
|
||||
self:OnUpdate(0)
|
||||
self.isAnimating = true
|
||||
self:TweenZOffsetTo(0, ANIMATE_OUT_DURATION, Util:GetEaseInOutQuad(), function()
|
||||
if callback then callback() end
|
||||
self.isAnimating = false
|
||||
end)
|
||||
end
|
||||
|
||||
function WindowFrame:SetPopOut(popOut)
|
||||
if self.isAnimating then
|
||||
return
|
||||
end
|
||||
|
||||
if popOut then
|
||||
self.isPopping = true
|
||||
self:TweenZOffsetTo(POPOUT_DISTANCE, POPOUT_DURATION, Util:GetEaseInOutQuad(), function()
|
||||
self.isPopping = false
|
||||
end)
|
||||
else
|
||||
self.isPopping = true
|
||||
self:TweenZOffsetTo(0, POPOUT_DURATION, Util:GetEaseInOutQuad(), function()
|
||||
self.isPopping = false
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function WindowFrame:OnUpdate(dt)
|
||||
self.panel.localCF = self.zeroCF * CFrame.new(0, 0, -self.zOffset)
|
||||
|
||||
if self.isPopping then
|
||||
local alpha = math.max(0, math.min(1, self.zOffset / POPOUT_DISTANCE))
|
||||
self.titlebar.ImageColor3 = BLURRED_TITLEBAR_COLOR:lerp(FOCUSED_TITLEBAR_COLOR, alpha)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--Notifications panel setup
|
||||
do
|
||||
notificationsPanel:SetType(Panel3D.Type.Fixed)
|
||||
notificationsPanel:SetVisible(false)
|
||||
notificationsPanel:SetCanFade(false)
|
||||
notificationsPanel:ResizeStuds(leftPanelWidth, totalHeight, PIXELS_PER_STUD)
|
||||
local notificationsFrame = Util:Create "TextButton" {
|
||||
Parent = notificationsPanel:GetGUI(),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, -4, 1, 0),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
Text = "",
|
||||
|
||||
Selectable = true,
|
||||
SelectionImageObject = emptySelectionImage
|
||||
}
|
||||
notificationsWindow = WindowFrame.new(notificationsPanel, notificationsFrame, "Notifications")
|
||||
notificationsWindow:AddCloseButton(function()
|
||||
NotificationHubModule:SetVisible(false)
|
||||
end)
|
||||
function notificationsPanel:OnUpdate(dt)
|
||||
notificationsWindow:OnUpdate(dt)
|
||||
end
|
||||
end
|
||||
|
||||
--Details panel setup
|
||||
do
|
||||
detailsPanel:SetType(Panel3D.Type.Fixed)
|
||||
detailsPanel:SetVisible(false)
|
||||
detailsPanel:SetCanFade(false)
|
||||
detailsPanel:ResizeStuds(rightPanelWidth, totalHeight, PIXELS_PER_STUD)
|
||||
local detailsFrame = Util:Create "TextButton" {
|
||||
Parent = detailsPanel:GetGUI(),
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
Text = "",
|
||||
|
||||
Selectable = true,
|
||||
SelectionImageObject = emptySelectionImage
|
||||
}
|
||||
detailsWindow = WindowFrame.new(detailsPanel, detailsFrame, "Friend Requests")
|
||||
function detailsPanel:OnUpdate(dt)
|
||||
detailsWindow:OnUpdate(dt)
|
||||
end
|
||||
end
|
||||
|
||||
local notificationsGroups = {}
|
||||
local notificationsGroupsList = {}
|
||||
local function groupSort(a, b)
|
||||
return a.order < b.order
|
||||
end
|
||||
local activeGroup = nil
|
||||
|
||||
local function layoutNotificationsGroups()
|
||||
local y = NOTIFICATION_PADDING_Y
|
||||
for _, group in ipairs(notificationsGroupsList) do
|
||||
if #group.notifications > 0 then
|
||||
local height = NOTIFICATION_HEIGHT_OFFSET + (NOTIFICATION_PADDING_Y * (math.min(MAX_NOTIFICATIONS_SHOWN, #group.notifications) - 1))
|
||||
local widthOffset = -((MAX_NOTIFICATIONS_SHOWN - 1) * NOTIFICATION_PADDING_Y)
|
||||
group.frame.Position = UDim2.new(NOTIFICATION_PADDING_X_SCALE, 0, 0, y)
|
||||
group.frame.Size = UDim2.new(NOTIFICATION_WIDTH_SCALE, widthOffset, 0, height)
|
||||
y = y + height + NOTIFICATION_PADDING_Y
|
||||
|
||||
if group.notificationsDirty then
|
||||
group.notificationsDirty = false
|
||||
|
||||
local notificationOffset = 0
|
||||
local notificationDepth = 0
|
||||
|
||||
local notificationsEnd = #group.notifications
|
||||
if notificationsEnd > 0 then
|
||||
local notificationsStart = math.max(1, notificationsEnd - MAX_NOTIFICATIONS_SHOWN + 1)
|
||||
for i = 1, notificationsEnd do
|
||||
local notification = group.notifications[i]
|
||||
if i >= notificationsStart then
|
||||
notification.frame.Visible = true
|
||||
notification.frame.Position = UDim2.new(0, notificationOffset, 0, notificationOffset)
|
||||
notificationOffset = notificationOffset + NOTIFICATION_PADDING_Y
|
||||
|
||||
local subpanel = notificationsPanel:SetSubpanelDepth(notification.frame, notificationDepth)
|
||||
notificationDepth = notificationDepth + NOTIFICATION_DEPTH_OFFSET
|
||||
else
|
||||
notification.frame.Visible = false
|
||||
end
|
||||
end
|
||||
|
||||
if activeGroup == group then
|
||||
notificationsStart = math.max(1, notificationsEnd - MAX_DETAILS_SHOWN + 1)
|
||||
local detailY = 0
|
||||
local fraction = 1 / MAX_DETAILS_SHOWN
|
||||
for i = 1, notificationsEnd do
|
||||
local notification = group.notifications[i]
|
||||
if i >= notificationsStart then
|
||||
notification.detailsFrame.Visible = true
|
||||
notification.detailsFrame.Position = UDim2.new(0, DETAILS_PADDING, detailY, DETAILS_PADDING)
|
||||
notification.detailsFrame.Size = UDim2.new(1, -DETAILS_PADDING * 2, fraction, -DETAILS_PADDING * 2)
|
||||
|
||||
detailY = detailY + fraction
|
||||
else
|
||||
notification.detailsFrame.Visible = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local NotificationGroup = {}
|
||||
do
|
||||
local NotificationGroup_mt = { __index = NotificationGroup }
|
||||
function NotificationGroup.new(key, title, order)
|
||||
local self = setmetatable({}, NotificationGroup_mt)
|
||||
|
||||
self.key = key
|
||||
self.title = title
|
||||
self.order = order
|
||||
self.notifications = {}
|
||||
self.notificationsDirty = false
|
||||
self.frame = Util:Create "Frame" {
|
||||
Parent = notificationsWindow.content,
|
||||
BackgroundTransparency = 1
|
||||
}
|
||||
self.detailsFrame = Util:Create "Frame" {
|
||||
Parent = nil,
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, 0, 1, 0)
|
||||
}
|
||||
|
||||
notificationsGroups[key] = self
|
||||
table.insert(notificationsGroupsList, self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function NotificationGroup:Deactivate()
|
||||
self.detailsFrame.Parent = nil
|
||||
for i, v in pairs(self.detailsFrame:GetChildren()) do
|
||||
detailsPanel:RemoveSubpanel(v)
|
||||
end
|
||||
end
|
||||
|
||||
function NotificationGroup:SwitchTo()
|
||||
detailsWindow:SetTitle(self.title)
|
||||
for i, v in pairs(notificationsGroups) do
|
||||
if v ~= self then
|
||||
v:Deactivate()
|
||||
end
|
||||
end
|
||||
self.detailsFrame.Parent = detailsWindow.content
|
||||
|
||||
activeGroup = self
|
||||
self.notificationsDirty = true
|
||||
end
|
||||
|
||||
function NotificationGroup:BringNotificationToFront(notification)
|
||||
if activeGroup ~= self then
|
||||
self:SwitchTo()
|
||||
end
|
||||
|
||||
if #self.notifications ~= 0 and notification == self.notifications[#self.notifications] then
|
||||
layoutNotificationsGroups()
|
||||
return --already on top, no point
|
||||
end
|
||||
|
||||
for i, v in ipairs(self.notifications) do
|
||||
if v == notification then
|
||||
--take it out
|
||||
table.remove(self.notifications, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
--put it back on top
|
||||
table.insert(self.notifications, notification)
|
||||
self.notificationsDirty = true
|
||||
layoutNotificationsGroups()
|
||||
end
|
||||
|
||||
function NotificationGroup:RemoveNotification(notification)
|
||||
for i, v in ipairs(self.notifications) do
|
||||
if v == notification then
|
||||
table.remove(self.notifications, i)
|
||||
notificationsPanel:RemoveSubpanel(notification.frame)
|
||||
detailsPanel:RemoveSubpanel(notification.detailsFrame)
|
||||
notification.detailsFrame:Destroy()
|
||||
notification.frame:Destroy()
|
||||
|
||||
self.notificationsDirty = true
|
||||
layoutNotificationsGroups()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function NotificationGroup:GetTopNotification()
|
||||
local numNotifications = #self.notifications
|
||||
if numNotifications <= 0 then
|
||||
return nil
|
||||
end
|
||||
return self.notifications[numNotifications]
|
||||
end
|
||||
end
|
||||
|
||||
NotificationGroup.new("Friends", "Friends", 1)
|
||||
NotificationGroup.new("BadgeAwards", "Badges", 2)
|
||||
NotificationGroup.new("PlayerPoints", "Points", 3)
|
||||
NotificationGroup.new("Developer", "Other", 4)
|
||||
table.sort(notificationsGroupsList, groupSort)
|
||||
|
||||
local function doCallback(callback, ...)
|
||||
if not callback then
|
||||
return
|
||||
end
|
||||
|
||||
if type(callback) == "function" then
|
||||
callback(...)
|
||||
return
|
||||
end
|
||||
if callback:IsA("BindableEvent") then
|
||||
callback:Fire(...)
|
||||
return
|
||||
end
|
||||
if callback:IsA("BindableFunction") then
|
||||
callback:Invoke(...)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local Notification = {}
|
||||
do
|
||||
local Notification_mt = { __index = Notification }
|
||||
function Notification.new(group, notificationInfo)
|
||||
local self = setmetatable({}, Notification_mt)
|
||||
self.group = group
|
||||
self.frame = Util:Create "ImageButton" {
|
||||
Parent = group.frame,
|
||||
Size = UDim2.new(1, 0, 0, NOTIFICATION_HEIGHT_OFFSET),
|
||||
|
||||
SelectionImageObject = emptySelectionImage,
|
||||
|
||||
BackgroundTransparency = 1 --when we have proper frame rendering with AA, we can change this and remove the stand-in background
|
||||
}
|
||||
self.frame.MouseButton1Click:connect(function()
|
||||
self:OnClicked()
|
||||
end)
|
||||
self.background = Util:Create "ImageLabel" { --this is the stand-in background for that smoooooooth edge rendering
|
||||
Parent = self.frame,
|
||||
Position = UDim2.new(0, -1, 0, -1),
|
||||
Size = UDim2.new(1, 2, 1, 2),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
Image = "rbxasset://textures/ui/vr/rectBackgroundWhite.png",
|
||||
ImageColor3 = NOTIFICATION_BG_COLOR,
|
||||
ImageTransparency = NOTIFICATION_BG_TRANSPARENCY,
|
||||
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(10, 10, 10, 10)
|
||||
}
|
||||
self.imageBackground = Util:Create "ImageLabel" {
|
||||
Parent = self.frame,
|
||||
Position = UDim2.new(0, 5, 0, 5),
|
||||
Size = UDim2.new(0, 70, 0, 70),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = "rbxasset://textures/ui/VR/circleWhite.png",
|
||||
ImageColor3 = notificationInfo.imgBackgroundColor or Color3.new(1, 1, 1)
|
||||
}
|
||||
self.image = Util:Create "ImageLabel" {
|
||||
Parent = self.imageBackground,
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = notificationInfo.Image
|
||||
}
|
||||
|
||||
local text = notificationInfo.Text
|
||||
if notificationInfo.Title and notificationInfo.Text then
|
||||
text = ("%s\n%s"):format(notificationInfo.Title, notificationInfo.Text)
|
||||
end
|
||||
self.text = Util:Create "TextLabel" {
|
||||
Parent = self.frame,
|
||||
|
||||
Position = UDim2.new(0, NOTIFICATION_HEIGHT_OFFSET, 0, 0),
|
||||
Size = UDim2.new(1, -NOTIFICATION_HEIGHT_OFFSET, 1, 0),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Text = text,
|
||||
Font = Enum.Font.SourceSans,
|
||||
FontSize = Enum.FontSize.Size18,
|
||||
TextColor3 = TEXT_COLOR
|
||||
}
|
||||
|
||||
self.detailsFrame = Util:Create "Frame" {
|
||||
Parent = group.detailsFrame,
|
||||
BackgroundTransparency = 1
|
||||
}
|
||||
self.detailsFrame.MouseEnter:connect(function()
|
||||
detailsPanel:SetSubpanelDepth(self.detailsFrame, 0.25)
|
||||
end)
|
||||
self.detailsFrame.MouseLeave:connect(function()
|
||||
detailsPanel:SetSubpanelDepth(self.detailsFrame, 0)
|
||||
end)
|
||||
self.detailsBackground = Util:Create "ImageLabel" {
|
||||
Parent = self.detailsFrame,
|
||||
Position = UDim2.new(0, -1, 0, -1),
|
||||
Size = UDim2.new(1, 2, 1, 2),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = "rbxasset://textures/ui/VR/rectBackgroundWhite.png",
|
||||
ImageColor3 = Color3.new(0.2, 0.2, 0.2),
|
||||
ImageTransparency = 0.1,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(10,10,10,10)
|
||||
}
|
||||
|
||||
self.detailsIconBackground = Util:Create "ImageLabel" {
|
||||
Parent = self.detailsFrame,
|
||||
Position = UDim2.new(0, 20, 0, 10),
|
||||
Size = UDim2.new(0, 80, 0, 80),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = "rbxasset://textures/ui/VR/circleWhite.png",
|
||||
ImageColor3 = notificationInfo.imgBackgroundColor or Color3.new(1, 1, 1)
|
||||
}
|
||||
self.detailsIcon = Util:Create "ImageLabel" {
|
||||
Parent = self.detailsIconBackground,
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = notificationInfo.Image
|
||||
}
|
||||
|
||||
local detailText = notificationInfo.DetailText or notificationInfo.Title
|
||||
self.detailsText = Util:Create "TextLabel" {
|
||||
Parent = self.detailsFrame,
|
||||
Position = UDim2.new(0, 110, 0, 10),
|
||||
Size = UDim2.new(1, -120, 0, 90),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Text = detailText,
|
||||
TextColor3 = TEXT_COLOR,
|
||||
TextXAlignment = Enum.TextXAlignment.Left,
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size36
|
||||
}
|
||||
|
||||
local function createButton(xPosScale, xSizeScale, text)
|
||||
local button, text = Util:Create "ImageButton" {
|
||||
Parent = self.detailsFrame,
|
||||
Position = UDim2.new(xPosScale, 0, BUTTON_Y_POS, 0),
|
||||
Size = UDim2.new(xSizeScale, 0, BUTTON_Y_SIZE, 0),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = BUTTON_NORMAL_IMG,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
SliceCenter = Rect.new(8,6,46,44),
|
||||
|
||||
SelectionImageObject = emptySelectionImage
|
||||
}, Util:Create "TextLabel" {
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, 0, 1, -6),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Text = text,
|
||||
TextColor3 = TEXT_COLOR,
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
FontSize = Enum.FontSize.Size24
|
||||
}
|
||||
text.Parent = button
|
||||
|
||||
button.SelectionGained:connect(function()
|
||||
button.Image = BUTTON_SELECTED_IMG
|
||||
end)
|
||||
button.SelectionLost:connect(function()
|
||||
button.Image = BUTTON_NORMAL_IMG
|
||||
end)
|
||||
return button, text
|
||||
end
|
||||
|
||||
if notificationInfo.Button1Text and notificationInfo.Button2Text then
|
||||
self.detailsButton1, self.detailsButton1Text = createButton(BUTTON_1_POS, BUTTON_DOUBLE_SIZE, notificationInfo.Button1Text)
|
||||
self.detailsButton2, self.detailsButton2Text = createButton(BUTTON_2_POS, BUTTON_DOUBLE_SIZE, notificationInfo.Button2Text)
|
||||
|
||||
self.detailsButton1.MouseButton1Click:connect(function()
|
||||
doCallback(notificationInfo.Callback, notificationInfo.Button1Text)
|
||||
self:Dismiss()
|
||||
end)
|
||||
self.detailsButton2.MouseButton1Click:connect(function()
|
||||
doCallback(notificationInfo.Callback, notificationInfo.Button2Text)
|
||||
self:Dismiss()
|
||||
end)
|
||||
elseif not notificationInfo.button2Text then
|
||||
local text = notificationInfo.Button1Text or "Dismiss"
|
||||
self.detailsButton1, self.detailsButton1Text = createButton(BUTTON_1_POS, BUTTON_SINGLE_SIZE, text)
|
||||
self.detailsButton1.MouseButton1Click:connect(function()
|
||||
doCallback(notificationInfo.Callback, notificationInfo.Button1Text)
|
||||
self:Dismiss()
|
||||
end)
|
||||
end
|
||||
|
||||
table.insert(group.notifications, self)
|
||||
group.notificationsDirty = true
|
||||
layoutNotificationsGroups()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function Notification:OnClicked()
|
||||
self.group:BringNotificationToFront(self)
|
||||
end
|
||||
|
||||
function Notification:Dismiss()
|
||||
self.group:RemoveNotification(self)
|
||||
end
|
||||
end
|
||||
|
||||
--NotificationHubModule API and state management
|
||||
do
|
||||
local pendingNotifications = {}
|
||||
local isVisible = false
|
||||
local unreadCount = 0
|
||||
|
||||
local SendNotificationInfoEvent = RobloxGui:WaitForChild("SendNotificationInfo")
|
||||
SendNotificationInfoEvent.Event:connect(function(notificationInfo)
|
||||
local group = notificationsGroups[notificationInfo.GroupName or "Developer"]
|
||||
if not group then
|
||||
group = notificationsGroups.Developer
|
||||
end
|
||||
|
||||
Notification.new(group, notificationInfo)
|
||||
|
||||
if not isVisible then
|
||||
unreadCount = unreadCount + 1
|
||||
NotificationHubModule.UnreadCountChanged(unreadCount)
|
||||
end
|
||||
end)
|
||||
|
||||
NotificationHubModule.VisibilityStateChanged = Util:Create "BindableEvent" {
|
||||
Name = "VisibilityStateChanged"
|
||||
}
|
||||
|
||||
function NotificationHubModule:GetNumberOfPendingNotifications()
|
||||
return #pendingNotifications
|
||||
end
|
||||
|
||||
function NotificationHubModule:IsVisible()
|
||||
return isVisible
|
||||
end
|
||||
|
||||
function NotificationHubModule:SetVisible(visible)
|
||||
if isVisible == visible then
|
||||
return
|
||||
end
|
||||
isVisible = visible
|
||||
|
||||
local topbarPanel = Panel3D.Get("Topbar3D")
|
||||
topbarPanel:SetCanFade(not visible)
|
||||
if visible then
|
||||
unreadCount = 0
|
||||
NotificationHubModule.UnreadCountChanged(unreadCount)
|
||||
|
||||
local hubSpace = topbarPanel.localCF * PANEL_OFFSET_CFRAME
|
||||
|
||||
notificationsPanel.localCF = hubSpace * CFrame.new(leftOffset, 0, 0)
|
||||
notificationsWindow.zeroCF = notificationsPanel.localCF
|
||||
if not NO_TRANSITION_ANIMATIONS then
|
||||
notificationsWindow:AnimateIn(nil)
|
||||
end
|
||||
|
||||
detailsPanel.localCF = hubSpace * CFrame.new(rightOffset, 0, 0)
|
||||
detailsWindow.zeroCF = detailsPanel.localCF
|
||||
if not NO_TRANSITION_ANIMATIONS then
|
||||
detailsWindow:AnimateIn(nil)
|
||||
end
|
||||
|
||||
notificationsPanel:SetVisible(true)
|
||||
detailsPanel:SetVisible(true)
|
||||
|
||||
VRHub:FireModuleOpened(NotificationHubModule.ModuleName)
|
||||
else
|
||||
if not NO_TRANSITION_ANIMATIONS then
|
||||
spawn(function()
|
||||
cancelAnimation = false
|
||||
notificationsWindow:AnimateOut(function()
|
||||
notificationsPanel:SetVisible(false)
|
||||
end)
|
||||
detailsWindow:AnimateOut(function()
|
||||
detailsPanel:SetVisible(false)
|
||||
end)
|
||||
end)
|
||||
else
|
||||
notificationsPanel:SetVisible(false)
|
||||
detailsPanel:SetVisible(false)
|
||||
end
|
||||
|
||||
VRHub:FireModuleClosed(NotificationHubModule.ModuleName)
|
||||
end
|
||||
|
||||
NotificationHubModule.VisibilityStateChanged:Fire(visible)
|
||||
end
|
||||
|
||||
|
||||
VRHub.ModuleOpened.Event:connect(function(moduleName, isExclusive, shouldCloseNonExclusive, shouldKeepTopbarOpen)
|
||||
if moduleName ~= NotificationHubModule.ModuleName then
|
||||
NotificationHubModule:SetVisible(false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return NotificationHubModule
|
||||
@@ -0,0 +1,85 @@
|
||||
-- NotifierHint3D.lua --
|
||||
-- Written by Kip Turner, copyright ROBLOX 2016 --
|
||||
|
||||
local NotifierHint = {}
|
||||
|
||||
local CoreGui = game:GetService('CoreGui')
|
||||
local RunService = game:GetService('RunService')
|
||||
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
|
||||
local Util = require(RobloxGui.Modules.Settings.Utility)
|
||||
|
||||
|
||||
local NotificationObject = Util:Create'ImageLabel'
|
||||
{
|
||||
Name = 'NotificationObject';
|
||||
-- These numbers are a bit funny to fit the screen of ROBLOX VR
|
||||
Position = UDim2.new(0.5, -860, 1, -1 - 300);
|
||||
Size = UDim2.new(0, 1700,0, 700 + 300);
|
||||
BackgroundTransparency = 1;
|
||||
Image = "rbxasset://textures/ui/VR/notifier_glow.png";
|
||||
ImageTransparency = 0;
|
||||
BorderSizePixel = 0;
|
||||
}
|
||||
|
||||
NotifierHint.DEFAULT_DURATION = 5
|
||||
|
||||
local function CreateNotificationEffect()
|
||||
local this = {}
|
||||
|
||||
local speed = 2.5
|
||||
local MAX_OPACITY = 0.5
|
||||
local WAVE_START_SHIFT = math.pi/2
|
||||
|
||||
local renderConn = nil
|
||||
|
||||
function this:Init(duration)
|
||||
local start = tick()
|
||||
local endTime = start + duration
|
||||
|
||||
if renderConn then
|
||||
renderConn:disconnect()
|
||||
renderConn = nil
|
||||
end
|
||||
renderConn = RunService.RenderStepped:connect(function()
|
||||
local now = tick()
|
||||
if now >= endTime then
|
||||
self:Cancel()
|
||||
return
|
||||
end
|
||||
NotificationObject.Parent = RobloxGui
|
||||
local tweenPositionOnSineWave = math.sin((tick() - start) * speed + WAVE_START_SHIFT)
|
||||
-- Restrict the sine wave to only positive values
|
||||
tweenPositionOnSineWave = (tweenPositionOnSineWave + 1) / 2
|
||||
-- Keep the transparency in the range
|
||||
NotificationObject.ImageTransparency = tweenPositionOnSineWave * (1 - MAX_OPACITY) + MAX_OPACITY
|
||||
end)
|
||||
end
|
||||
|
||||
function this:Cancel()
|
||||
if renderConn then
|
||||
renderConn:disconnect()
|
||||
renderConn = nil
|
||||
end
|
||||
NotificationObject.Parent = nil
|
||||
end
|
||||
|
||||
return this
|
||||
end
|
||||
|
||||
local NotifierEffect = CreateNotificationEffect()
|
||||
|
||||
function NotifierHint:BeginNotification(duration)
|
||||
self:CancelNotification()
|
||||
NotifierEffect:Init(duration or self.DEFAULT_DURATION)
|
||||
end
|
||||
|
||||
function NotifierHint:CancelNotification()
|
||||
NotifierEffect:Cancel()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
return NotifierHint
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
local InputService = game:GetService("UserInputService")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
|
||||
local VRHub = require(RobloxGui.Modules.VR.VRHub)
|
||||
local Util = require(RobloxGui.Modules.Settings.Utility)
|
||||
|
||||
local RecenterModule = {}
|
||||
RecenterModule.ModuleName = "Recenter"
|
||||
RecenterModule.KeepVRTopbarOpen = true
|
||||
RecenterModule.VRIsExclusive = true
|
||||
RecenterModule.VRClosesNonExclusive = false
|
||||
VRHub:RegisterModule(RecenterModule)
|
||||
|
||||
local countdownPanel = Panel3D.Get("RecenterCountdown")
|
||||
countdownPanel:SetType(Panel3D.Type.HorizontalFollow)
|
||||
countdownPanel:ResizeStuds(5, 3, 128)
|
||||
countdownPanel:SetCanFade(false)
|
||||
|
||||
local countdown = Util:Create "TextLabel" {
|
||||
Parent = countdownPanel:GetGUI(),
|
||||
|
||||
Position = UDim2.new(0.5, -64, 0.5, -64),
|
||||
Size = UDim2.new(0, 128, 0, 128),
|
||||
|
||||
BackgroundTransparency = 0.9,
|
||||
BackgroundColor3 = Color3.new(0.2, 0.2, 0.2),
|
||||
|
||||
TextColor3 = Color3.new(1, 1, 1),
|
||||
Text = "",
|
||||
TextScaled = true,
|
||||
Font = Enum.Font.SourceSansBold,
|
||||
|
||||
Visible = true
|
||||
}
|
||||
local recenterFrame = Util:Create "ImageLabel" {
|
||||
Parent = countdownPanel:GetGUI(),
|
||||
|
||||
Position = UDim2.new(0, 0, 0, 0),
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Image = "rbxasset://textures/ui/VR/recenterFrame.png"
|
||||
}
|
||||
|
||||
countdownPanel:SetVisible(false)
|
||||
|
||||
local isCountingDown = false
|
||||
|
||||
VRHub.ModuleOpened.Event:connect(function(moduleName)
|
||||
if moduleName ~= RecenterModule.ModuleName then
|
||||
local module = VRHub:GetModule(moduleName)
|
||||
if module.VRIsExclusive then
|
||||
isCountingDown = false
|
||||
countdownPanel:SetVisible(false)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function RecenterModule:SetVisible(visible)
|
||||
if visible then
|
||||
if isCountingDown then
|
||||
isCountingDown = false
|
||||
VRHub:FireModuleClosed(RecenterModule.ModuleName)
|
||||
return
|
||||
else
|
||||
VRHub:FireModuleOpened(RecenterModule.ModuleName)
|
||||
end
|
||||
|
||||
spawn(function()
|
||||
isCountingDown = true
|
||||
countdownPanel:SetVisible(true)
|
||||
|
||||
for i = 3, 1, -1 do
|
||||
if isCountingDown then
|
||||
countdown.Text = tostring(i)
|
||||
wait(1)
|
||||
end
|
||||
end
|
||||
|
||||
if isCountingDown then
|
||||
InputService:RecenterUserHeadCFrame()
|
||||
end
|
||||
|
||||
countdownPanel:SetVisible(false)
|
||||
isCountingDown = false
|
||||
|
||||
VRHub:FireModuleClosed(RecenterModule.ModuleName)
|
||||
end)
|
||||
else
|
||||
VRHub:FireModuleClosed(RecenterModule.ModuleName)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return RecenterModule
|
||||
@@ -0,0 +1,87 @@
|
||||
local InputService = game:GetService("UserInputService")
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local Panel3D = require(RobloxGui.Modules.VR.Panel3D)
|
||||
local VRHub = require(RobloxGui.Modules.VR.VRHub)
|
||||
local VRKeyboard = require(RobloxGui.Modules.VR.VirtualKeyboard)
|
||||
|
||||
local PANEL_OFFSET_CFRAME = CFrame.Angles(math.rad(-5), 0, 0) * CFrame.new(0, 5, 0) * CFrame.Angles(math.rad(-15), 0, 0)
|
||||
|
||||
local UserGuiModule = {}
|
||||
UserGuiModule.ModuleName = "UserGui"
|
||||
UserGuiModule.KeepVRTopbarOpen = false
|
||||
UserGuiModule.VRIsExclusive = false
|
||||
UserGuiModule.VRClosesNonExclusive = false
|
||||
VRHub:RegisterModule(UserGuiModule)
|
||||
|
||||
local userGuiPanel = Panel3D.Get(UserGuiModule.ModuleName)
|
||||
userGuiPanel:SetType(Panel3D.Type.Fixed)
|
||||
userGuiPanel:ResizeStuds(4, 4, 128)
|
||||
userGuiPanel:SetVisible(false)
|
||||
|
||||
VRHub.ModuleOpened.Event:connect(function(moduleName)
|
||||
if moduleName ~= UserGuiModule.ModuleName then
|
||||
local module = VRHub:GetModule(moduleName)
|
||||
if module.VRClosesNonExclusive and userGuiPanel:IsVisible() then
|
||||
UserGuiModule:SetVisible(false)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local KeyboardOpen = false
|
||||
local GuiVisible = false
|
||||
|
||||
function UserGuiModule:SetVisible(visible)
|
||||
GuiVisible = visible
|
||||
userGuiPanel:SetVisible(GuiVisible)
|
||||
if GuiVisible then
|
||||
local topbarPanel = Panel3D.Get("Topbar3D")
|
||||
local userGuiModuleCFrame = topbarPanel.localCF * PANEL_OFFSET_CFRAME
|
||||
|
||||
userGuiPanel.localCF = userGuiModuleCFrame
|
||||
VRHub:FireModuleOpened(UserGuiModule.ModuleName)
|
||||
else
|
||||
VRHub:FireModuleClosed(UserGuiModule.ModuleName)
|
||||
end
|
||||
|
||||
local success, msg = pcall(function()
|
||||
-- We need to hide the UserGui when typing on the keyboard so that the textbox doesn't sink events from the keyboard
|
||||
local showGui = GuiVisible and not KeyboardOpen
|
||||
CoreGui:SetUserGuiRendering(true, showGui and userGuiPanel:GetPart() or nil, Enum.NormalId.Front)
|
||||
end)
|
||||
end
|
||||
|
||||
function UserGuiModule:Update()
|
||||
self:SetVisible(GuiVisible)
|
||||
end
|
||||
|
||||
local function OnVREnabled(prop)
|
||||
if prop == 'VREnabled' then
|
||||
local guiPart = nil
|
||||
if InputService.VREnabled then
|
||||
if userGuiPanel.isVisible then
|
||||
guiPart = userGuiPanel:GetPart()
|
||||
end
|
||||
else
|
||||
userGuiPanel:SetVisible(false)
|
||||
end
|
||||
local success, msg = pcall(function()
|
||||
CoreGui:SetUserGuiRendering(InputService.VREnabled, guiPart, Enum.NormalId.Front)
|
||||
end)
|
||||
|
||||
VRKeyboard.OpenedEvent:connect(function()
|
||||
KeyboardOpen = true
|
||||
UserGuiModule:Update()
|
||||
end)
|
||||
|
||||
VRKeyboard.ClosedEvent:connect(function()
|
||||
KeyboardOpen = false
|
||||
UserGuiModule:Update()
|
||||
end)
|
||||
|
||||
end
|
||||
end
|
||||
InputService.Changed:connect(OnVREnabled)
|
||||
spawn(function() OnVREnabled("VREnabled") end)
|
||||
|
||||
return UserGuiModule
|
||||
@@ -0,0 +1,63 @@
|
||||
--Modules/VR/VRHub.lua
|
||||
--Handles all global VR state that isn't built into a specific module.
|
||||
--Written by 0xBAADF00D (Kyle) on 6/10/16
|
||||
local CoreGui = game:GetService("CoreGui")
|
||||
local RobloxGui = CoreGui:WaitForChild("RobloxGui")
|
||||
local Util = require(RobloxGui.Modules.Settings.Utility)
|
||||
|
||||
local VRHub = {}
|
||||
local RegisteredModules = {}
|
||||
local OpenModules = {}
|
||||
|
||||
function VRHub:RegisterModule(module)
|
||||
RegisteredModules[module.ModuleName] = module
|
||||
end
|
||||
|
||||
function VRHub:GetModule(moduleName)
|
||||
return RegisteredModules[moduleName]
|
||||
end
|
||||
|
||||
function VRHub:IsModuleOpened(moduleName)
|
||||
return OpenModules[moduleName] ~= nil
|
||||
end
|
||||
|
||||
VRHub.ModuleOpened = Util:Create "BindableEvent" {
|
||||
Name = "VRModuleOpened"
|
||||
}
|
||||
--Wrapper function to document the arguments to the event
|
||||
function VRHub:FireModuleOpened(moduleName)
|
||||
if not RegisteredModules[moduleName] then
|
||||
error("Tried to open module that is not registered: " .. moduleName)
|
||||
end
|
||||
|
||||
if OpenModules[moduleName] ~= RegisteredModules[moduleName] then
|
||||
OpenModules[moduleName] = RegisteredModules[moduleName]
|
||||
VRHub.ModuleOpened:Fire(moduleName)
|
||||
end
|
||||
end
|
||||
|
||||
VRHub.ModuleClosed = Util:Create "BindableEvent" {
|
||||
Name = "VRModuleClosed"
|
||||
}
|
||||
--Wrapper function to document the arguments to the event
|
||||
function VRHub:FireModuleClosed(moduleName)
|
||||
if not RegisteredModules[moduleName] then
|
||||
error("Tried to close module that is not registered: " .. moduleName)
|
||||
end
|
||||
|
||||
if OpenModules[moduleName] ~= nil then
|
||||
OpenModules[moduleName] = nil
|
||||
VRHub.ModuleClosed:Fire(moduleName)
|
||||
end
|
||||
end
|
||||
|
||||
function VRHub:KeepVRTopbarOpen()
|
||||
for moduleName, openModule in pairs(OpenModules) do
|
||||
if openModule.KeepVRTopbarOpen then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return VRHub
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user