mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-27 08:00:51 +01:00
Merge branch 'dev' of https://github.com/billsonnn/nitro-react into dev
This commit is contained in:
commit
4d5915fa98
File diff suppressed because one or more lines are too long
BIN
src/assets/images/icons/camera-colormatrix.png
Normal file
BIN
src/assets/images/icons/camera-colormatrix.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 249 B |
BIN
src/assets/images/icons/camera-composite.png
Normal file
BIN
src/assets/images/icons/camera-composite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 204 B |
Binary file not shown.
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 183 B |
Binary file not shown.
Before Width: | Height: | Size: 154 B |
@ -446,6 +446,18 @@ i {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&.icon-camera-colormatrix {
|
||||
background: url('../images/icons/camera-colormatrix.png');
|
||||
width: 32px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
&.icon-camera-composite {
|
||||
background: url('../images/icons/camera-composite.png');
|
||||
width: 32px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
&.spin {
|
||||
animation: rotating 1s linear infinite;
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
import classNames from 'classnames';
|
||||
import { FC } from 'react';
|
||||
import { NitroCardContentViewProps } from './NitroCardContextView.types';
|
||||
|
||||
export const NitroCardContentView: FC<NitroCardContentViewProps> = props =>
|
||||
{
|
||||
const { isDark = false } = props;
|
||||
const { className = null } = props;
|
||||
|
||||
return (
|
||||
<div className={ 'container-fluid bg-light content-area ' + classNames({ 'bg-light': !isDark, 'bg-dark': isDark }) }>
|
||||
<div className={ 'container-fluid bg-light content-area ' + className }>
|
||||
{ props.children }
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
export interface NitroCardContentViewProps
|
||||
{
|
||||
isDark?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ export const AvatarInfoWidgetView: FC<AvatarInfoWidgetViewProps> = props =>
|
||||
return value;
|
||||
});
|
||||
|
||||
return [ ...existing, new RoomObjectNameData(nameData.objectId, nameData.category, nameData.id, nameData.name, nameData.type) ]
|
||||
return [ ...existing, nameData ]
|
||||
});
|
||||
}
|
||||
return;
|
||||
|
@ -1,12 +1,24 @@
|
||||
.nitro-camera {
|
||||
.nitro-camera-capture {
|
||||
.camera-canvas {
|
||||
width: 340px;
|
||||
height: 462px;
|
||||
|
||||
background-image: url('../../../../assets/images/room-widgets/camera-widget/camera-spritesheet.png');
|
||||
|
||||
.camera-frame {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
position: absolute;
|
||||
width: 318px;
|
||||
height: 318px;
|
||||
margin-top: 9px;
|
||||
margin-left: 11.4px;
|
||||
|
||||
&.bg {
|
||||
background: black;
|
||||
}
|
||||
|
||||
.camera-frame-preview-actions {
|
||||
background: rgba(0, 0, 0, .5);
|
||||
}
|
||||
}
|
||||
|
||||
.camera-button {
|
||||
@ -26,4 +38,57 @@
|
||||
background-position: -340px -188px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.camera-roll {
|
||||
width: 330px;
|
||||
background: #bab8b4;
|
||||
border-bottom-left-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
border: 1px solid black;
|
||||
box-shadow: inset 1px 0px white, inset -1px -1px white;
|
||||
|
||||
img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-camera-editor {
|
||||
width: 600px;
|
||||
|
||||
.effects {
|
||||
max-height: 354px;
|
||||
|
||||
.effect-thumbnail {
|
||||
border-color: $grid-border-color !important;
|
||||
background-color: $grid-bg-color;
|
||||
|
||||
&.active {
|
||||
border-color: $grid-active-border-color !important;
|
||||
background-color: $grid-active-bg-color;
|
||||
}
|
||||
|
||||
.effect-thumbnail-image {
|
||||
background: black;
|
||||
|
||||
img {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.picture-preview {
|
||||
width: 318px;
|
||||
height: 318px;
|
||||
|
||||
&.zoomed {
|
||||
background-size: 636px;
|
||||
background-position: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,72 +1,75 @@
|
||||
import { NitroRectangle } from 'nitro-renderer';
|
||||
import { FC, useCallback, useRef, useState } from 'react';
|
||||
import { GetRoomEngine, GetRoomSession } from '../../../../api';
|
||||
import { RoomCameraWidgetEditorEffect } from 'nitro-renderer/src/nitro/room/camera-widget/RoomCameraWidgetEditorEffect';
|
||||
import { RoomCameraWidgetManagerEvent } from 'nitro-renderer/src/nitro/room/events/RoomCameraWidgetManagerEvent';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetRoomEngine } from '../../../../api';
|
||||
import { RoomWidgetCameraEvent } from '../../../../events/room-widgets/camera/RoomWidgetCameraEvent';
|
||||
import { DraggableWindow } from '../../../../hooks/draggable-window/DraggableWindow';
|
||||
import { useRoomEngineEvent } from '../../../../hooks/events/nitro/room/room-engine-event';
|
||||
import { useUiEvent } from '../../../../hooks/events/ui/ui-event';
|
||||
import { CameraWidgetViewProps } from './CameraWidgetView.types';
|
||||
import { CameraWidgetCaptureView } from './views/capture/CameraWidgetCaptureView';
|
||||
import { CameraWidgetEditorView } from './views/editor/CameraWidgetEditorView';
|
||||
|
||||
export const CameraWidgetView: FC<CameraWidgetViewProps> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const cameraFrameRef = useRef<HTMLDivElement>();
|
||||
const [ isCaptureVisible, setIsCaptureVisible ] = useState(false);
|
||||
const [ isEditorVisible, setIsEditorVisible ] = useState(false);
|
||||
const [ chosenPicture, setChosenPicture ] = useState<HTMLImageElement>(null);
|
||||
const [ availableEffects, setAvailableEffects ] = useState<RoomCameraWidgetEditorEffect[]>(null);
|
||||
|
||||
const onRoomWidgetCameraEvent = useCallback((event: RoomWidgetCameraEvent) =>
|
||||
const getAvailableEffects = useCallback(() =>
|
||||
{
|
||||
if(GetRoomEngine().roomCameraWidgetManager.isLoaded)
|
||||
{
|
||||
setAvailableEffects(Array.from(GetRoomEngine().roomCameraWidgetManager.loadedEffects.values()));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case RoomWidgetCameraEvent.SHOW_CAMERA:
|
||||
setIsVisible(true);
|
||||
setIsCaptureVisible(true);
|
||||
getAvailableEffects();
|
||||
return;
|
||||
case RoomWidgetCameraEvent.HIDE_CAMERA:
|
||||
setIsVisible(false);
|
||||
setIsCaptureVisible(false);
|
||||
setIsEditorVisible(false);
|
||||
return;
|
||||
case RoomWidgetCameraEvent.TOGGLE_CAMERA:
|
||||
setIsVisible(value => !value);
|
||||
setIsEditorVisible(false);
|
||||
setIsCaptureVisible(value => !value);
|
||||
getAvailableEffects();
|
||||
return;
|
||||
case RoomCameraWidgetManagerEvent.INITIALIZED:
|
||||
getAvailableEffects();
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
useUiEvent(RoomWidgetCameraEvent.SHOW_CAMERA, onRoomWidgetCameraEvent);
|
||||
useUiEvent(RoomWidgetCameraEvent.HIDE_CAMERA, onRoomWidgetCameraEvent);
|
||||
useUiEvent(RoomWidgetCameraEvent.TOGGLE_CAMERA, onRoomWidgetCameraEvent);
|
||||
useUiEvent(RoomWidgetCameraEvent.SHOW_CAMERA, onNitroEvent);
|
||||
useUiEvent(RoomWidgetCameraEvent.HIDE_CAMERA, onNitroEvent);
|
||||
useUiEvent(RoomWidgetCameraEvent.TOGGLE_CAMERA, onNitroEvent);
|
||||
useRoomEngineEvent(RoomCameraWidgetManagerEvent.INITIALIZED, onNitroEvent);
|
||||
|
||||
const processAction = useCallback((type: string, value: string = null) =>
|
||||
const processAction = useCallback((type: string, value: any = null) =>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case 'close':
|
||||
setIsVisible(false);
|
||||
setIsCaptureVisible(false);
|
||||
setIsEditorVisible(false);
|
||||
return;
|
||||
case 'capture_choose_picture':
|
||||
setChosenPicture(value);
|
||||
setIsCaptureVisible(false);
|
||||
setIsEditorVisible(true);
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const takePicture = useCallback(() =>
|
||||
{
|
||||
const frameBounds = cameraFrameRef.current.getBoundingClientRect();
|
||||
|
||||
if(!frameBounds) return;
|
||||
|
||||
const rectangle = new NitroRectangle(Math.floor(frameBounds.x), Math.floor(frameBounds.y), Math.floor(frameBounds.width), Math.floor(frameBounds.height));
|
||||
|
||||
GetRoomEngine().createRoomScreenshot(GetRoomSession().roomId, 1, rectangle);
|
||||
}, []);
|
||||
|
||||
if(!isVisible) return null;
|
||||
|
||||
return (
|
||||
<DraggableWindow handle=".nitro-camera">
|
||||
<div className="nitro-camera">
|
||||
<div className="overflow-auto">
|
||||
<div className="cursor-pointer float-end me-3 mt-2" onClick={ event => processAction('close') }>
|
||||
<i className="fas fa-times"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex justify-content-center">
|
||||
<div ref={ cameraFrameRef } className="camera-frame"></div>
|
||||
<div className="camera-button" onClick={ takePicture }></div>
|
||||
</div>
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
( isCaptureVisible && <CameraWidgetCaptureView onCloseClick={ () => processAction('close') } onChoosePicture={ (picture) => processAction('capture_choose_picture', picture) } /> ) ||
|
||||
( isEditorVisible && <CameraWidgetEditorView onCloseClick={ () => processAction('close') } picture={ chosenPicture } availableEffects={ availableEffects } /> )
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
import classNames from 'classnames';
|
||||
import { NitroRectangle } from 'nitro-renderer';
|
||||
import { FC, useCallback, useRef, useState } from 'react';
|
||||
import { GetRoomEngine } from '../../../../../../api/nitro/room/GetRoomEngine';
|
||||
import { GetRoomSession } from '../../../../../../api/nitro/session/GetRoomSession';
|
||||
import { DraggableWindow } from '../../../../../../hooks/draggable-window/DraggableWindow';
|
||||
import { LocalizeText } from '../../../../../../utils/LocalizeText';
|
||||
import { CameraWidgetCaptureViewProps } from './CameraWidgetCaptureView.types';
|
||||
|
||||
export const CameraWidgetCaptureView: FC<CameraWidgetCaptureViewProps> = props =>
|
||||
{
|
||||
const CAMERA_ROLL_LIMIT: number = 5;
|
||||
|
||||
const [ picturesTaken, setPicturesTaken ] = useState<HTMLImageElement[]>([]);
|
||||
const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1);
|
||||
const cameraFrameRef = useRef<HTMLDivElement>();
|
||||
|
||||
const takePicture = useCallback(() =>
|
||||
{
|
||||
if(selectedPictureIndex > -1)
|
||||
{
|
||||
setSelectedPictureIndex(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
const frameBounds = cameraFrameRef.current.getBoundingClientRect();
|
||||
|
||||
if(!frameBounds) return;
|
||||
|
||||
const rectangle = new NitroRectangle(Math.floor(frameBounds.x), Math.floor(frameBounds.y), Math.floor(frameBounds.width), Math.floor(frameBounds.height));
|
||||
|
||||
const image = GetRoomEngine().createRoomScreenshot(GetRoomSession().roomId, 1, rectangle);
|
||||
|
||||
if(picturesTaken.length + 1 === CAMERA_ROLL_LIMIT)
|
||||
{
|
||||
alert(LocalizeText('camera.full.body'));
|
||||
}
|
||||
|
||||
if(picturesTaken.length === CAMERA_ROLL_LIMIT)
|
||||
{
|
||||
setPicturesTaken(picturesTaken => [ ...picturesTaken.slice(0, CAMERA_ROLL_LIMIT - 1), image ]);
|
||||
}
|
||||
else
|
||||
{
|
||||
setPicturesTaken(picturesTaken => [ ...picturesTaken, image ]);
|
||||
}
|
||||
}, [ picturesTaken, selectedPictureIndex ]);
|
||||
|
||||
const processAction = useCallback((type: string, value: string | number = null) =>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case 'take_picture':
|
||||
takePicture();
|
||||
return;
|
||||
case 'preview_picture':
|
||||
setSelectedPictureIndex(Number(value));
|
||||
return;
|
||||
case 'discard_picture':
|
||||
setSelectedPictureIndex(-1);
|
||||
const newPicturesTaken = picturesTaken;
|
||||
picturesTaken.splice(selectedPictureIndex, 1);
|
||||
setPicturesTaken(newPicturesTaken);
|
||||
return;
|
||||
case 'edit_picture':
|
||||
props.onChoosePicture(picturesTaken[selectedPictureIndex]);
|
||||
return;
|
||||
}
|
||||
}, [ picturesTaken, selectedPictureIndex ]);
|
||||
|
||||
return (
|
||||
<DraggableWindow handle=".nitro-camera-capture">
|
||||
<div className="nitro-camera-capture d-flex flex-column justify-content-center align-items-center">
|
||||
<div className="camera-canvas">
|
||||
<div className="overflow-auto">
|
||||
<div className="cursor-pointer float-end me-3 mt-2" onClick={ props.onCloseClick }>
|
||||
<i className="fas fa-times"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div ref={ cameraFrameRef } className={'camera-frame ' + classNames({'bg': selectedPictureIndex > -1}) }>
|
||||
{ selectedPictureIndex > -1 && <div>
|
||||
<img src={ picturesTaken[selectedPictureIndex].src } />
|
||||
<div className="camera-frame-preview-actions w-100 position-absolute bottom-0 py-2 text-center">
|
||||
<button className="btn btn-success me-3" onClick={ event => processAction('edit_picture') }>{ LocalizeText('camera.editor.button.text') }</button>
|
||||
<button className="btn btn-danger" onClick={ event => processAction('discard_picture') }>{ LocalizeText('camera.delete.button.text') }</button>
|
||||
</div>
|
||||
</div> }
|
||||
</div>
|
||||
<div className="d-flex justify-content-center">
|
||||
<div className="camera-button" onClick={ takePicture }></div>
|
||||
</div>
|
||||
</div>
|
||||
{ picturesTaken.length > 0 && <div className="camera-roll d-flex justify-content-center py-2">
|
||||
{ picturesTaken.map((picture, index) =>
|
||||
{
|
||||
return <img key={ index } className={ (index < picturesTaken.length - 1 ? 'me-2' : '') } src={ picture.src } onClick={ event => processAction('preview_picture', index) } />;
|
||||
}) }
|
||||
</div> }
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface CameraWidgetCaptureViewProps
|
||||
{
|
||||
onCloseClick: () => void;
|
||||
onChoosePicture: (picture: HTMLImageElement) => void;
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
import classNames from 'classnames';
|
||||
import { RoomCameraWidgetEditorSelectedEffect } from 'nitro-renderer/src/nitro/room/camera-widget/RoomCameraWidgetEditorSelectedEffect';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { GetRoomEngine } from '../../../../../../api';
|
||||
import { NitroCardContentView } from '../../../../../../layout/card/content/NitroCardContentView';
|
||||
import { NitroCardHeaderView } from '../../../../../../layout/card/header/NitroCardHeaderView';
|
||||
import { NitroCardView } from '../../../../../../layout/card/NitroCardView';
|
||||
import { NitroCardTabsView } from '../../../../../../layout/card/tabs/NitroCardTabsView';
|
||||
import { NitroCardTabsItemView } from '../../../../../../layout/card/tabs/tabs-item/NitroCardTabsItemView';
|
||||
import { LocalizeText } from '../../../../../../utils/LocalizeText';
|
||||
import { CameraWidgetEditorTabs, CameraWidgetEditorViewProps } from './CameraWidgetEditorView.types';
|
||||
|
||||
export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
{
|
||||
const TABS: string[] = [ CameraWidgetEditorTabs.COLORMATRIX, CameraWidgetEditorTabs.COMPOSITE ];
|
||||
const MY_LEVEL: number = 0;
|
||||
|
||||
const [ currentTab, setCurrentTab ] = useState(CameraWidgetEditorTabs.COLORMATRIX);
|
||||
const [ isZoomed, setIsZoomed ] = useState(false);
|
||||
const [ selectedEffects, setSelectedEffects ] = useState<RoomCameraWidgetEditorSelectedEffect[]>([]);
|
||||
const [ effectsThumbnails, setEffectThumbnails ] = useState<{name: string, image: HTMLImageElement}[]>([]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const thumbnails = [];
|
||||
|
||||
for(const effect of props.availableEffects)
|
||||
{
|
||||
let alpha = 126;
|
||||
|
||||
if(effect.colorMatrix.length > 0) alpha = 0.5;
|
||||
|
||||
thumbnails.push({name: effect.name, image: GetRoomEngine().roomCameraWidgetManager.editImage(props.picture, [new RoomCameraWidgetEditorSelectedEffect(effect, alpha)])});
|
||||
}
|
||||
|
||||
setEffectThumbnails(thumbnails);
|
||||
}, [ props.availableEffects ]);
|
||||
|
||||
const getEffectThumbnail = useCallback((effectName: string) =>
|
||||
{
|
||||
const search = effectsThumbnails.find(thumbnail => thumbnail.name === effectName);
|
||||
|
||||
if(search) return search.image.src;
|
||||
|
||||
return null;
|
||||
}, [ effectsThumbnails ]);
|
||||
|
||||
const getEffectList = useCallback(() =>
|
||||
{
|
||||
if(currentTab === CameraWidgetEditorTabs.COLORMATRIX)
|
||||
{
|
||||
return props.availableEffects.filter(effect => effect.colorMatrix.length > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return props.availableEffects.filter(effect => effect.colorMatrix.length === 0);
|
||||
}
|
||||
}, [ currentTab, props.availableEffects ]);
|
||||
|
||||
const processAction = useCallback((type: string, value: string | number = null) =>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case 'close':
|
||||
props.onCloseClick();
|
||||
return;
|
||||
case 'change_tab':
|
||||
setCurrentTab(String(value));
|
||||
return;
|
||||
case 'zoom':
|
||||
setIsZoomed(isZoomed => !isZoomed);
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-camera-editor">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } />
|
||||
<div className="d-flex">
|
||||
<div className="w-100">
|
||||
<NitroCardTabsView>
|
||||
{ TABS.map(tab =>
|
||||
{
|
||||
return <NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ event => processAction('change_tab', tab) }><i className={ 'icon icon-camera-' + tab }></i></NitroCardTabsItemView>
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex h-100 overflow-auto effects px-2">
|
||||
<div className="row row-cols-3">
|
||||
{ getEffectList().map(effect =>
|
||||
{
|
||||
return (
|
||||
<div key={ effect.name } className="col mb-3">
|
||||
<div title={ LocalizeText('camera.effect.name.' + effect.name) } className="effect-thumbnail cursor-pointer position-relative border border-2 rounded d-flex flex-column justify-content-center align-items-center py-1">
|
||||
<div className="effect-thumbnail-image rounded">
|
||||
<img className="rounded" src={ getEffectThumbnail(effect.name) } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</div>
|
||||
<div className="w-100">
|
||||
<NitroCardTabsView></NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="bg-black rounded">
|
||||
<div className={ 'picture-preview rounded' + classNames({ ' zoomed': isZoomed }) } style={ { backgroundImage: 'url(' + props.picture.src + ')' } }></div>
|
||||
</div>
|
||||
<div className="d-flex mt-2">
|
||||
<button className="btn btn-primary btn-sm me-2">{ LocalizeText('save') }</button>
|
||||
<button className="btn btn-primary btn-sm" onClick={ event => processAction('zoom') }>{ LocalizeText('room.zoom.button.text') }</button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</div>
|
||||
</div>
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex justify-content-end">
|
||||
<button className="btn btn-primary me-2">{ LocalizeText('generic.cancel') }</button>
|
||||
<button className="btn btn-success">{ LocalizeText('camera.preview.button.text') }</button>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
import { RoomCameraWidgetEditorEffect } from '../../../../../../../../nitro-renderer/src/nitro/room/camera-widget/RoomCameraWidgetEditorEffect';
|
||||
|
||||
export interface CameraWidgetEditorViewProps
|
||||
{
|
||||
onCloseClick: () => void;
|
||||
picture: HTMLImageElement;
|
||||
availableEffects: RoomCameraWidgetEditorEffect[];
|
||||
}
|
||||
|
||||
export class CameraWidgetEditorTabs
|
||||
{
|
||||
public static readonly COLORMATRIX: string = 'colormatrix';
|
||||
public static readonly COMPOSITE: string = 'composite';
|
||||
}
|
Loading…
Reference in New Issue
Block a user