diff --git a/code/__DEFINES/paintings.dm b/code/__DEFINES/paintings.dm new file mode 100644 index 00000000000..0ce657c88f1 --- /dev/null +++ b/code/__DEFINES/paintings.dm @@ -0,0 +1,6 @@ +/// Only returns paintings with 23x23 or 24x24 sizes fitting AI display icon. +#define PAINTINGS_FILTER_AI_PORTRAIT (1<<0) +/// Search mode for the title of the painting +#define PAINTINGS_FILTER_SEARCH_TITLE (1<<1) +/// Ditto but for the creator's name +#define PAINTINGS_FILTER_SEARCH_CREATOR (1<<2) diff --git a/code/controllers/subsystem/persistent_paintings.dm b/code/controllers/subsystem/persistent_paintings.dm index b474d89fc1b..3e213d1575b 100644 --- a/code/controllers/subsystem/persistent_paintings.dm +++ b/code/controllers/subsystem/persistent_paintings.dm @@ -100,9 +100,6 @@ new_data["frame_type"] = frame_type return new_data -/// Only returns paintings with 23x23 or 24x24 sizes fitting AI display icon. -#define PAINTINGS_FILTER_AI_PORTRAIT 1 - SUBSYSTEM_DEF(persistent_paintings) name = "Persistent Paintings" init_order = INIT_ORDER_PERSISTENT_PAINTINGS @@ -147,18 +144,33 @@ SUBSYSTEM_DEF(persistent_paintings) return ..() -/// Generates painting data ready to be consumed by ui -/datum/controller/subsystem/persistent_paintings/proc/painting_ui_data(filter=NONE,admin=FALSE) +/** + * Generates painting data ready to be consumed by ui. + * Args: + * * filter: a bitfield argument is used to filter out paintings that don't match certain requisites. + * * admin : whether all the json data of the painting is added to the return value or only the more IC details + * * search_text : text to search for if the PAINTINGS_FILTER_SEARCH_TITLE or PAINTINGS_FILTER_SEARCH_CREATOR filters are enabled. + */ +/datum/controller/subsystem/persistent_paintings/proc/painting_ui_data(filter=NONE, admin=FALSE, search_text) . = list() + var/searching = filter & (PAINTINGS_FILTER_SEARCH_TITLE|PAINTINGS_FILTER_SEARCH_CREATOR) && search_text for(var/datum/painting/painting as anything in paintings) if(filter & PAINTINGS_FILTER_AI_PORTRAIT && ((painting.width != 24 && painting.width != 23) || (painting.height != 24 && painting.height != 23))) continue + if(searching) + var/haystack_text = "" + if(filter & PAINTINGS_FILTER_SEARCH_TITLE) + haystack_text = painting.title + else if(filter & PAINTINGS_FILTER_SEARCH_CREATOR) + haystack_text = painting.creator_name + if(!findtext(haystack_text, search_text)) + continue if(admin) var/list/pdata = painting.to_json() pdata["ref"] = REF(painting) . += list(pdata) else - . += list(list("title" = painting.title,"md5" = painting.md5,"ref" = REF(painting))) + . += list(list("title" = painting.title, "creator" = painting.creator_name, "md5" = painting.md5,"ref" = REF(painting))) /// Returns paintings with given tag. /datum/controller/subsystem/persistent_paintings/proc/get_paintings_with_tag(tag_name) diff --git a/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm b/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm index 2b7912921ae..b64c0e38996 100644 --- a/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm +++ b/code/modules/mob/living/silicon/ai/ai_portrait_picker.dm @@ -6,7 +6,14 @@ * very similar to centcom_podlauncher in terms of how this is coded, so i kept a lot of comments from it */ /datum/portrait_picker - var/client/holder //client of whoever is using this datum + /// Client of whoever is using this datum + var/client/holder + /// The last input in the search tab. + var/search_string + /// Whether the search function will check the title of the painting or the author's name. + var/search_mode = PAINTINGS_FILTER_SEARCH_TITLE + /// Stores the result of the search. + var/list/matching_paintings /datum/portrait_picker/New(user)//user can either be a client or a mob due to byondcode(tm) if (istype(user, /client)) @@ -35,7 +42,9 @@ /datum/portrait_picker/ui_data(mob/user) var/list/data = list() - data["paintings"] = SSpersistent_paintings.painting_ui_data(filter = PAINTINGS_FILTER_AI_PORTRAIT) + data["paintings"] = matching_paintings || SSpersistent_paintings.painting_ui_data(filter = PAINTINGS_FILTER_AI_PORTRAIT) + data["search_string"] = search_string + data["search_mode"] = search_mode == PAINTINGS_FILTER_SEARCH_TITLE ? "Title" : "Author" return data /datum/portrait_picker/ui_act(action, params) @@ -43,6 +52,15 @@ if(.) return switch(action) + if("search") + if(search_string != params["to_search"]) + search_string = params["to_search"] + generate_matching_paintings_list() + . = TRUE + if("change_search_mode") + search_mode = search_mode == PAINTINGS_FILTER_SEARCH_TITLE ? PAINTINGS_FILTER_SEARCH_CREATOR : PAINTINGS_FILTER_SEARCH_TITLE + generate_matching_paintings_list() + . = TRUE if("select") //var/list/tab2key = list(TAB_LIBRARY = "library", TAB_SECURE = "library_secure", TAB_PRIVATE = "library_private") //var/folder = tab2key[params["tab"]] @@ -70,3 +88,9 @@ ai.cut_overlays() //so people can't keep repeatedly select portraits to add stacking overlays ai.icon_state = "ai-portrait-active"//background ai.add_overlay(MA) + +/datum/portrait_picker/proc/generate_matching_paintings_list() + matching_paintings = null + if(!search_string) + return + matching_paintings = SSpersistent_paintings.painting_ui_data(filter = PAINTINGS_FILTER_AI_PORTRAIT|search_mode, search_text = search_string) diff --git a/code/modules/modular_computers/file_system/programs/portrait_printer.dm b/code/modules/modular_computers/file_system/programs/portrait_printer.dm index da6e0b9e2e8..23c403034f5 100644 --- a/code/modules/modular_computers/file_system/programs/portrait_printer.dm +++ b/code/modules/modular_computers/file_system/programs/portrait_printer.dm @@ -2,6 +2,7 @@ ///how much paper it takes from the printer to create a canvas. #define CANVAS_PAPER_COST 10 + /** * ## portrait printer! * @@ -20,10 +21,21 @@ size = 9 tgui_id = "NtosPortraitPrinter" program_icon = "paint-brush" + /** + * The last input in the search tab, stored here and reused in the UI to show successive users if + * the current list of paintings is limited to the results of a search or not. + */ + var/search_string + /// Whether the search function will check the title of the painting or the author's name. + var/search_mode = PAINTINGS_FILTER_SEARCH_TITLE + /// Stores the result of the search, for later access. + var/list/matching_paintings /datum/computer_file/program/portrait_printer/ui_data(mob/user) var/list/data = list() - data["paintings"] = SSpersistent_paintings.painting_ui_data() + data["paintings"] = matching_paintings || SSpersistent_paintings.painting_ui_data() + data["search_string"] = search_string + data["search_mode"] = search_mode == PAINTINGS_FILTER_SEARCH_TITLE ? "Title" : "Author" return data /datum/computer_file/program/portrait_printer/ui_assets(mob/user) @@ -35,7 +47,26 @@ . = ..() if(.) return + switch(action) + if("search") + if(search_string != params["to_search"]) + search_string = params["to_search"] + generate_matching_paintings_list() + . = TRUE + if("change_search_mode") + search_mode = search_mode == PAINTINGS_FILTER_SEARCH_TITLE ? PAINTINGS_FILTER_SEARCH_CREATOR : PAINTINGS_FILTER_SEARCH_TITLE + generate_matching_paintings_list() + . = TRUE + if("select") + print_painting(params["selected"]) +/datum/computer_file/program/portrait_printer/proc/generate_matching_paintings_list() + matching_paintings = null + if(!search_string) + return + matching_paintings = SSpersistent_paintings.painting_ui_data(filter = search_mode, search_text = search_string) + +/datum/computer_file/program/portrait_printer/proc/print_painting(selected_painting) //printer check! var/obj/item/computer_hardware/printer/printer if(computer) @@ -49,7 +80,7 @@ printer.stored_paper -= CANVAS_PAPER_COST //canvas printing! - var/datum/painting/chosen_portrait = locate(params["selected"]) in SSpersistent_paintings.paintings + var/datum/painting/chosen_portrait = locate(selected_painting) in SSpersistent_paintings.paintings var/png = "data/paintings/images/[chosen_portrait.md5].png" var/icon/art_icon = new(png) @@ -75,3 +106,5 @@ printed_canvas.update_icon() to_chat(usr, span_notice("You have printed [chosen_portrait.title] onto a new canvas.")) playsound(computer.physical, 'sound/items/poster_being_created.ogg', 100, TRUE) + +#undef CANVAS_PAPER_COST diff --git a/tgstation.dme b/tgstation.dme index 80d13630ab4..5ece6649b7d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -119,6 +119,7 @@ #include "code\__DEFINES\networks.dm" #include "code\__DEFINES\nitrile.dm" #include "code\__DEFINES\obj_flags.dm" +#include "code\__DEFINES\paintings.dm" #include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\plumbing.dm" diff --git a/tgui/packages/tgui/interfaces/NtosPortraitPrinter.js b/tgui/packages/tgui/interfaces/NtosPortraitPrinter.js index f5ca231e7a4..16d7e308c08 100644 --- a/tgui/packages/tgui/interfaces/NtosPortraitPrinter.js +++ b/tgui/packages/tgui/interfaces/NtosPortraitPrinter.js @@ -1,6 +1,6 @@ import { resolveAsset } from '../assets'; import { useBackend, useLocalState } from '../backend'; -import { Button, NoticeBox, Section, Stack } from '../components'; +import { Button, NoticeBox, Section, Stack, Input } from '../components'; import { NtosWindow } from '../layouts'; export const NtosPortraitPrinter = (props, context) => { @@ -8,16 +8,45 @@ export const NtosPortraitPrinter = (props, context) => { const [listIndex, setListIndex] = useLocalState(context, 'listIndex', 0); const { paintings, + search_string, + search_mode, } = data; - const current_portrait_title = paintings[listIndex]["title"]; - const current_portrait_asset_name = "paintings" + "_" + paintings[listIndex]["md5"]; + const got_paintings = !!paintings.length; + const current_portrait_title = got_paintings + && paintings[listIndex]["title"]; + const current_portrait_author = got_paintings + && "By " + paintings[listIndex]["creator"]; + const current_portrait_asset_name = got_paintings + && "paintings" + "_" + paintings[listIndex]["md5"]; return ( + height={446}> + +
+ { + act('search', { + to_search: value, + }); + setListIndex(0); + }} /> +
+
{ align="center" justify="center" direction="column"> - - - - - {current_portrait_title} - + {got_paintings ? ( + <> + + + + + {current_portrait_title} + + + {current_portrait_author} + + + ) : ( + + No paintings found. + + )}
@@ -64,6 +104,7 @@ export const NtosPortraitPrinter = (props, context) => {