mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-25 04:57:47 +01:00
TGUI Update Hotfix
This commit is contained in:
@@ -37,6 +37,8 @@
|
||||
var/datum/tgui_state/state = null
|
||||
/// Rate limit client refreshes to prevent DoS.
|
||||
COOLDOWN_DECLARE(refresh_cooldown)
|
||||
/// Are byond mouse events beyond the window passed in to the ui
|
||||
var/mouse_hooked = FALSE
|
||||
/// The map z-level to display.
|
||||
var/map_z_level = 1
|
||||
/// The Parent UI
|
||||
@@ -108,6 +110,8 @@
|
||||
window.send_message("update", get_payload(
|
||||
with_data = TRUE,
|
||||
with_static_data = TRUE))
|
||||
if(mouse_hooked)
|
||||
window.set_mouse_macro()
|
||||
SStgui.on_open(src)
|
||||
|
||||
/**
|
||||
@@ -147,6 +151,17 @@
|
||||
/datum/tgui/proc/set_autoupdate(autoupdate)
|
||||
src.autoupdate = autoupdate
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Enable/disable passing through byond mouse events to the window
|
||||
*
|
||||
* required value bool Enable/disable hooking.
|
||||
*/
|
||||
/datum/tgui/proc/set_mouse_hook(value)
|
||||
src.mouse_hooked = value
|
||||
//Handle unhooking/hooking on already open windows ?
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
@@ -328,7 +343,7 @@
|
||||
if(initialized)
|
||||
send_full_update()
|
||||
initialized = TRUE
|
||||
if("pingReply")
|
||||
if("ping/reply")
|
||||
initialized = TRUE
|
||||
if("suspend")
|
||||
close(can_be_suspended = TRUE)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
var/initial_inline_html
|
||||
var/initial_inline_js
|
||||
var/initial_inline_css
|
||||
var/mouse_event_macro_set = FALSE
|
||||
|
||||
/**
|
||||
* public
|
||||
@@ -215,6 +216,8 @@
|
||||
/datum/tgui_window/proc/close(can_be_suspended = TRUE, logout = FALSE)
|
||||
if(!client)
|
||||
return
|
||||
if(mouse_event_macro_set)
|
||||
remove_mouse_macro()
|
||||
if(can_be_suspended && can_be_suspended())
|
||||
#ifdef TGUI_DEBUGGING
|
||||
log_tgui(client, "[id]/close: suspending")
|
||||
@@ -364,7 +367,7 @@
|
||||
// If not locked, handle these message types
|
||||
switch(type)
|
||||
if("ping")
|
||||
send_message("pingReply", payload)
|
||||
send_message("ping/reply", payload)
|
||||
if("suspend")
|
||||
close(can_be_suspended = TRUE)
|
||||
if("close")
|
||||
@@ -377,3 +380,44 @@
|
||||
// Resend the assets
|
||||
for(var/asset in sent_assets)
|
||||
send_asset(asset)
|
||||
|
||||
/datum/tgui_window/vv_edit_var(var_name, var_value)
|
||||
return var_name != NAMEOF(src, id) && ..()
|
||||
|
||||
|
||||
/datum/tgui_window/proc/set_mouse_macro()
|
||||
if(mouse_event_macro_set)
|
||||
return
|
||||
|
||||
var/list/byondToTguiEventMap = list(
|
||||
"MouseDown" = "byond/mousedown",
|
||||
"MouseUp" = "byond/mouseup"
|
||||
)
|
||||
|
||||
for(var/mouseMacro in byondToTguiEventMap)
|
||||
var/command_template = ".output CONTROL PAYLOAD"
|
||||
var/event_message = TGUI_CREATE_MESSAGE(byondToTguiEventMap[mouseMacro], null)
|
||||
var target_control = is_browser \
|
||||
? "[id]:update" \
|
||||
: "[id].browser:update"
|
||||
var/with_id = replacetext(command_template, "CONTROL", target_control)
|
||||
var/full_command = replacetext(with_id, "PAYLOAD", event_message)
|
||||
|
||||
var/list/params = list()
|
||||
params["parent"] = "default" //Technically this is external to tgui but whatever
|
||||
params["name"] = mouseMacro
|
||||
params["command"] = full_command
|
||||
|
||||
winset(client, "[mouseMacro]Window[id]Macro", params)
|
||||
mouse_event_macro_set = TRUE
|
||||
|
||||
/datum/tgui_window/proc/remove_mouse_macro()
|
||||
if(!mouse_event_macro_set)
|
||||
stack_trace("Unsetting mouse macro on tgui window that has none")
|
||||
var/list/byondToTguiEventMap = list(
|
||||
"MouseDown" = "byond/mousedown",
|
||||
"MouseUp" = "byond/mouseup"
|
||||
)
|
||||
for(var/mouseMacro in byondToTguiEventMap)
|
||||
winset(client, null, "[mouseMacro]Window[id]Macro.parent=null")
|
||||
mouse_event_macro_set = FALSE
|
||||
|
||||
@@ -2,16 +2,15 @@ import { filter, sortBy } from 'common/collections';
|
||||
import { flow } from 'common/fp';
|
||||
import { classes } from 'common/react';
|
||||
import { createSearch } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, ByondUi, Input, Section, Dropdown } from '../components';
|
||||
import { refocusLayout, Window } from '../layouts';
|
||||
import { Button, ByondUi, Dropdown, Flex, Input, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
/**
|
||||
* Returns previous and next camera names relative to the currently
|
||||
* active camera.
|
||||
*/
|
||||
const prevNextCamera = (cameras, activeCamera) => {
|
||||
export const prevNextCamera = (cameras, activeCamera) => {
|
||||
if (!activeCamera) {
|
||||
return [];
|
||||
}
|
||||
@@ -26,9 +25,9 @@ const prevNextCamera = (cameras, activeCamera) => {
|
||||
*
|
||||
* Filters cameras, applies search terms and sorts the alphabetically.
|
||||
*/
|
||||
const selectCameras = (cameras, searchText = '', networkFilter = '') => {
|
||||
export const selectCameras = (cameras, searchText = '', networkFilter = '') => {
|
||||
const testSearch = createSearch(searchText, (camera) => camera.name);
|
||||
let fl = flow([
|
||||
return flow([
|
||||
// Null camera filter
|
||||
filter((camera) => camera?.name),
|
||||
// Optional search term
|
||||
@@ -39,20 +38,10 @@ const selectCameras = (cameras, searchText = '', networkFilter = '') => {
|
||||
// Slightly expensive, but way better than sorting in BYOND
|
||||
sortBy((camera) => camera.name),
|
||||
])(cameras);
|
||||
return fl;
|
||||
};
|
||||
|
||||
export const CameraConsole = (props, context) => {
|
||||
return (
|
||||
<Window width={870} height={708} resizable>
|
||||
<CameraConsoleContent />
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const CameraConsoleContent = (props, context) => {
|
||||
const { act, data, config } = useBackend(context);
|
||||
|
||||
const { act, data } = useBackend(context);
|
||||
const { mapRef, activeCamera } = data;
|
||||
const cameras = selectCameras(data.cameras);
|
||||
const [prevCameraName, nextCameraName] = prevNextCamera(
|
||||
@@ -60,10 +49,10 @@ export const CameraConsoleContent = (props, context) => {
|
||||
activeCamera
|
||||
);
|
||||
return (
|
||||
<Fragment>
|
||||
<Window width={870} height={708}>
|
||||
<div className="CameraConsole__left">
|
||||
<Window.Content scrollable>
|
||||
<CameraConsoleSearch />
|
||||
<CameraConsoleContent />
|
||||
</Window.Content>
|
||||
</div>
|
||||
<div className="CameraConsole__right">
|
||||
@@ -72,6 +61,26 @@ export const CameraConsoleContent = (props, context) => {
|
||||
{(activeCamera && activeCamera.name) || '—'}
|
||||
</div>
|
||||
<div className="CameraConsole__toolbarRight">
|
||||
SEL:
|
||||
<Button
|
||||
icon="chevron-left"
|
||||
disabled={!prevCameraName}
|
||||
onClick={() =>
|
||||
act('switch_camera', {
|
||||
name: prevCameraName,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
icon="chevron-right"
|
||||
disabled={!nextCameraName}
|
||||
onClick={() =>
|
||||
act('switch_camera', {
|
||||
name: nextCameraName,
|
||||
})
|
||||
}
|
||||
/>
|
||||
| PAN:
|
||||
<Button icon="chevron-left" onClick={() => act('pan', { dir: 8 })} />
|
||||
<Button icon="chevron-up" onClick={() => act('pan', { dir: 1 })} />
|
||||
<Button icon="chevron-right" onClick={() => act('pan', { dir: 4 })} />
|
||||
@@ -85,11 +94,11 @@ export const CameraConsoleContent = (props, context) => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const CameraConsoleSearch = (props, context) => {
|
||||
export const CameraConsoleContent = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const [searchText, setSearchText] = useLocalState(context, 'searchText', '');
|
||||
const [networkFilter, setNetworkFilter] = useLocalState(
|
||||
@@ -101,46 +110,52 @@ export const CameraConsoleSearch = (props, context) => {
|
||||
allNetworks.sort();
|
||||
const cameras = selectCameras(data.cameras, searchText, networkFilter);
|
||||
return (
|
||||
<Fragment>
|
||||
<Input
|
||||
fluid
|
||||
mb={1}
|
||||
placeholder="Search for a camera"
|
||||
onInput={(e, value) => setSearchText(value)}
|
||||
/>
|
||||
<Dropdown
|
||||
mb={1}
|
||||
width="177px"
|
||||
options={allNetworks}
|
||||
placeholder="No Filter"
|
||||
onSelected={(value) => setNetworkFilter(value)}
|
||||
/>
|
||||
<Section>
|
||||
{cameras.map((camera) => (
|
||||
// We're not using the component here because performance
|
||||
// would be absolutely abysmal (50+ ms for each re-render).
|
||||
<div
|
||||
key={camera.name}
|
||||
title={camera.name}
|
||||
className={classes([
|
||||
'Button',
|
||||
'Button--fluid',
|
||||
'Button--color--transparent',
|
||||
'Button--ellipsis',
|
||||
activeCamera &&
|
||||
camera.name === activeCamera.name &&
|
||||
'Button--selected',
|
||||
])}
|
||||
onClick={() => {
|
||||
refocusLayout();
|
||||
act('switch_camera', {
|
||||
name: camera.name,
|
||||
});
|
||||
}}>
|
||||
{camera.name}
|
||||
</div>
|
||||
))}
|
||||
</Section>
|
||||
</Fragment>
|
||||
<Flex direction={'column'} height="100%">
|
||||
<Flex.Item>
|
||||
<Input
|
||||
autoFocus
|
||||
fluid
|
||||
mt={1}
|
||||
placeholder="Search for a camera"
|
||||
onInput={(e, value) => setSearchText(value)}
|
||||
/>
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<Dropdown
|
||||
mb={1}
|
||||
width="177px"
|
||||
displayText={networkFilter || 'No Filter'}
|
||||
options={allNetworks}
|
||||
onSelected={(value) => setNetworkFilter(value)}
|
||||
/>
|
||||
</Flex.Item>
|
||||
<Flex.Item height="100%">
|
||||
<Section fill scrollable>
|
||||
{cameras.map((camera) => (
|
||||
// We're not using the component here because performance
|
||||
// would be absolutely abysmal (50+ ms for each re-render).
|
||||
<div
|
||||
key={camera.name}
|
||||
title={camera.name}
|
||||
className={classes([
|
||||
'Button',
|
||||
'Button--fluid',
|
||||
'Button--color--transparent',
|
||||
'Button--ellipsis',
|
||||
activeCamera &&
|
||||
camera.name === activeCamera.name &&
|
||||
'Button--selected',
|
||||
])}
|
||||
onClick={() =>
|
||||
act('switch_camera', {
|
||||
name: camera.name,
|
||||
})
|
||||
}>
|
||||
{camera.name}
|
||||
</div>
|
||||
))}
|
||||
</Section>
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user