Make floor painter UI nicer and move it to TGUI (#14635)

* Make Dropdown component use `selected` prop instead of state.

This makes the selection properly update when it's changed server-side.

* Make floor painter preview available directions nicely.

Also migrate it to tgui while at it.

* Have a preview of all available styles with pictures.

Also address the review comments.
This commit is contained in:
moxian
2020-10-21 00:12:38 -04:00
committed by GitHub
parent 9b5aa2ea53
commit ed9c3a22e1
4 changed files with 172 additions and 53 deletions
@@ -50,57 +50,65 @@
if(!user)
return 0
user.set_machine(src)
interact(user)
tgui_interact(user)
return 1
/obj/item/floor_painter/interact(mob/user as mob)
if(!floor_icon)
floor_icon = icon('icons/turf/floors.dmi', floor_state, floor_dir)
user << browse_rsc(floor_icon, "floor.png")
var/dat = {"
<center>
<a href="?src=[UID()];cycleleft=1">&lt;-</a>
<img style="-ms-interpolation-mode: nearest-neighbor;" src="floor.png" width=128 height=128 border=4>
<a href="?src=[UID()];cycleright=1">-&gt;</a>
</center>
<a href="?src=[UID()];choose_state=1">Choose Style</a>
<div class='statusDisplay'>Style: [floor_state]</div>
<a href="?src=[UID()];choose_dir=1">Choose Direction</a>
<div class='statusDisplay'>Direction: [dir2text(floor_dir)]</div>
"}
/obj/item/floor_painter/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null,
datum/tgui_state/state = GLOB.tgui_inventory_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "FloorPainter", name, 405, 470, master_ui, state)
// Disable automatic updates, because:
// 1) we are the only user of the item, and don't expect to observe external changes
// 2) generating and sending the icon each tick is a bit expensive, and creates small but noticeable lag
ui.set_autoupdate(FALSE)
ui.open()
var/datum/browser/popup = new(user, "floor_painter", name, 225, 300)
popup.set_content(dat)
popup.open()
/obj/item/floor_painter/tgui_data(mob/user)
var/list/data = list()
data["availableStyles"] = allowed_states
data["selectedStyle"] = floor_state
data["selectedDir"] = dir2text(floor_dir)
/obj/item/floor_painter/Topic(href, href_list)
data["directionsPreview"] = list()
for(var/dir in GLOB.alldirs)
var/icon/floor_icon = icon('icons/turf/floors.dmi', floor_state, dir)
data["directionsPreview"][dir2text(dir)] = icon2base64(floor_icon)
return data
/obj/item/floor_painter/tgui_static_data(mob/user)
var/list/data = list()
data["allStylesPreview"] = list()
for (var/style in allowed_states)
var/icon/floor_icon = icon('icons/turf/floors.dmi', style, SOUTH)
data["allStylesPreview"][style] = icon2base64(floor_icon)
return data
/obj/item/floor_painter/tgui_act(action, params)
if(..())
return
if(href_list["choose_state"])
var/state = input("Please select a style", "[src]") as null|anything in allowed_states
if(state)
floor_state = state
floor_dir = SOUTH // Reset dir, because some icon_states might not have that dir.
if(href_list["choose_dir"])
var/seldir = input("Please select a direction", "[src]") as null|anything in list("north", "south", "east", "west", "northeast", "northwest", "southeast", "southwest")
if(seldir)
floor_dir = text2dir(seldir)
if(href_list["cycleleft"])
var/index = allowed_states.Find(floor_state)
index--
if(index < 1)
index = allowed_states.len
floor_state = allowed_states[index]
floor_dir = SOUTH
if(href_list["cycleright"])
var/index = allowed_states.Find(floor_state)
index++
if(index > allowed_states.len)
index = 1
floor_state = allowed_states[index]
floor_dir = SOUTH
if(action == "select_style")
var/new_style = params["style"]
if (allowed_states.Find(new_style) != 0)
floor_state = new_style
floor_icon = icon('icons/turf/floors.dmi', floor_state, floor_dir)
if(usr)
attack_self(usr)
if(action == "cycle_style")
var/index = allowed_states.Find(floor_state)
index += text2num(params["offset"])
while(index < 1)
index += length(allowed_states)
while(index > length(allowed_states))
index -= length(allowed_states)
floor_state = allowed_states[index]
if(action == "select_direction")
var/dir = text2dir(params["direction"])
if (dir != 0)
floor_dir = dir
SStgui.update_uis(src)
+1 -5
View File
@@ -7,7 +7,6 @@ export class Dropdown extends Component {
constructor(props) {
super(props);
this.state = {
selected: props.selected,
open: false,
};
this.handleClick = () => {
@@ -33,9 +32,6 @@ export class Dropdown extends Component {
}
setSelected(selected) {
this.setState({
selected: selected,
});
this.setOpen(false);
this.props.onSelected(selected);
}
@@ -109,7 +105,7 @@ export class Dropdown extends Component {
this.setOpen(!this.state.open);
}}>
<span className="Dropdown__selected-text">
{this.state.selected}
{selected}
</span>
{!!nochevron || (
<span className="Dropdown__arrow-button">
@@ -0,0 +1,115 @@
import { useBackend, useLocalState } from '../backend';
import { Button, LabeledList, Section, Table, Dropdown, Flex, Icon, Box } from '../components';
import { Window } from '../layouts';
const SelectableTile = (props, context) => {
const { act, data } = useBackend(context);
const {
image,
isSelected,
onSelect,
} = props;
return (
<img
src={`data:image/jpeg;base64,${image}`}
style={{
"border-style":
(isSelected && "solid") || "none",
"border-width": "2px",
"border-color": "orange",
padding: (isSelected && "2px") || "4px",
}}
onClick={onSelect}
/>
);
};
export const FloorPainter = (props, context) => {
const { act, data } = useBackend(context);
const {
availableStyles,
selectedStyle,
selectedDir,
directionsPreview,
allStylesPreview,
} = data;
return (
<Window resizable>
<Window.Content scrollable>
<Section title="Decal setup">
<Flex>
<Flex.Item>
<Button
icon="chevron-left"
onClick={() => act("cycle_style", { offset: -1 })}
/>
</Flex.Item>
<Flex.Item>
<Dropdown
options={availableStyles}
selected={selectedStyle}
width="150px"
height="20px"
ml="2px"
mr="2px"
nochevron="true"
onSelected={val => act("select_style", { style: val })}
/>
</Flex.Item>
<Flex.Item>
<Button
icon="chevron-right"
onClick={() => act("cycle_style", { offset: 1 })}
/>
</Flex.Item>
</Flex>
<Box mt="5px" mb="5px">
<Flex
overflowY="auto" // scroll
maxHeight="220px" // a bit more than half of all tiles fit in this box at once.
wrap="wrap">
{availableStyles.map(style => (
<Flex.Item key="{style}" >
<SelectableTile
image={allStylesPreview[style]}
isSelected={selectedStyle===style}
onSelect={() => act("select_style", { style: style })}
/>
</Flex.Item>
))}
</Flex>
</Box>
<LabeledList>
<LabeledList.Item label="Direction">
<Table style={{ display: "inline" }}>
{["north", "", "south"].map(latitude => (
<Table.Row key={latitude}>
{[latitude + "west", latitude, latitude + "east"].map(
dir => (
<Table.Cell key={dir} style={{ "vertical-align": "middle", "text-align": "center" }}>
{dir === "" ? (
<Icon name="arrows-alt" size={3} />
) : (
<SelectableTile
image={directionsPreview[dir]}
isSelected={dir===selectedDir}
onSelect={() => act("select_direction", { direction: dir })}
/>
)}
</Table.Cell>
)
)}
</Table.Row>
))}
</Table>
</LabeledList.Item>
</LabeledList>
</Section>
</Window.Content>
</Window>
);
};
File diff suppressed because one or more lines are too long