Blender V4.5
asset_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "AS_asset_library.hh"
11
12#include "BKE_asset_edit.hh"
13#include "BKE_blendfile.hh"
14#include "BKE_bpath.hh"
15#include "BKE_context.hh"
16#include "BKE_global.hh"
17#include "BKE_icons.h"
18#include "BKE_lib_id.hh"
19#include "BKE_main.hh"
20#include "BKE_preferences.h"
21#include "BKE_preview_image.hh"
22#include "BKE_report.hh"
23#include "BKE_screen.hh"
24
25#include "BLI_fnmatch.h"
26#include "BLI_path_utils.hh"
27#include "BLI_rect.h"
28#include "BLI_set.hh"
29#include "BLI_string.h"
30
31#include "ED_asset.hh"
32#include "ED_screen.hh"
33/* XXX needs access to the file list, should all be done via the asset system in future. */
34#include "ED_fileselect.hh"
35#include "ED_render.hh"
36#include "ED_util.hh"
38
39#include "BLT_translation.hh"
40
41#include "RNA_access.hh"
42#include "RNA_define.hh"
43#include "RNA_prototypes.hh"
44
45#include "IMB_imbuf.hh"
46#include "IMB_thumbs.hh"
47
48#include "WM_api.hh"
49
50#include "DNA_space_types.h"
51
52#include "GPU_immediate.hh"
53#include "UI_interface_c.hh"
54#include "UI_resources.hh"
55
56namespace blender::ed::asset {
57/* -------------------------------------------------------------------- */
58
63struct IDVecStats {
64 bool has_asset = false;
65 bool has_supported_type = false;
66 bool is_single = false;
67};
68
74{
75 IDVecStats stats;
76
77 stats.is_single = id_pointers.size() == 1;
78
79 for (const PointerRNA &ptr : id_pointers) {
81
82 ID *id = static_cast<ID *>(ptr.data);
83 if (id_type_is_supported(id)) {
84 stats.has_supported_type = true;
85 }
86 if (ID_IS_ASSET(id)) {
87 stats.has_asset = true;
88 }
89 }
90
91 return stats;
92}
93
94static const char *asset_operation_unsupported_type_msg(const bool is_single)
95{
96 const char *msg_single =
97 "Data-block does not support asset operations - must be "
99 const char *msg_multiple =
100 "No data-block selected that supports asset operations - select at least "
102 return is_single ? msg_single : msg_multiple;
103}
104
105/* -------------------------------------------------------------------- */
106
108 public:
109 void operator()(const bContext &C, Span<PointerRNA> ids);
110
111 void reportResults(ReportList &reports) const;
112 bool wasSuccessful() const;
113
114 private:
115 struct Stats {
116 int tot_created = 0;
117 int tot_already_asset = 0;
118 ID *last_id = nullptr;
119 };
120
121 Stats stats;
122};
123
125{
126 for (const PointerRNA &ptr : ids) {
128
129 ID *id = static_cast<ID *>(ptr.data);
130 if (id->asset_data) {
131 stats.tot_already_asset++;
132 continue;
133 }
134
135 if (mark_id(id)) {
136 generate_preview(&C, id);
137
138 stats.last_id = id;
139 stats.tot_created++;
140 }
141 }
142}
143
145{
146 return stats.tot_created > 0;
147}
148
150{
151 /* User feedback on failure. */
152 if (!wasSuccessful()) {
153 if (stats.tot_already_asset > 0) {
155 RPT_ERROR,
156 "Selected data-blocks are already assets (or do not support use as assets)");
157 }
158 else {
160 RPT_ERROR,
161 "No data-blocks to create assets for found (or do not support use as assets)");
162 }
163 }
164 /* User feedback on success. */
165 else if (stats.tot_created == 1) {
166 /* If only one data-block: Give more useful message by printing asset name. */
167 BKE_reportf(&reports, RPT_INFO, "Data-block '%s' is now an asset", stats.last_id->name + 2);
168 }
169 else {
170 BKE_reportf(&reports, RPT_INFO, "%i data-blocks are now assets", stats.tot_created);
171 }
172}
173
175 const wmOperator *op,
176 const Span<PointerRNA> ids)
177{
178 AssetMarkHelper mark_helper;
179 mark_helper(*C, ids);
180 mark_helper.reportResults(*op->reports);
181
182 if (!mark_helper.wasSuccessful()) {
183 return OPERATOR_CANCELLED;
184 }
185
188
189 return OPERATOR_FINISHED;
190}
191
193{
195
196 if (!ctx_stats.has_supported_type) {
198 return false;
199 }
200
201 return true;
202}
203
205{
206 ot->name = "Mark as Asset";
207 ot->description =
208 "Enable easier reuse of selected data-blocks through the Asset Browser, with the help of "
209 "customizable metadata (like previews, descriptions and tags)";
210 ot->idname = "ASSET_OT_mark";
211
212 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
214 };
215 ot->poll = [](bContext *C) -> bool {
217 };
218
220}
221
226{
227 ot->name = "Mark as Single Asset";
228 ot->description =
229 "Enable easier reuse of a data-block through the Asset Browser, with the help of "
230 "customizable metadata (like previews, descriptions and tags)";
231 ot->idname = "ASSET_OT_mark_single";
232
233 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
235 };
236 ot->poll = [](bContext *C) -> bool {
238 };
239
241}
242
243/* -------------------------------------------------------------------- */
244
246 const bool set_fake_user_;
247
248 public:
249 AssetClearHelper(const bool set_fake_user) : set_fake_user_(set_fake_user) {}
250
252
253 void reportResults(const bContext *C, ReportList &reports) const;
254 bool wasSuccessful() const;
255
256 private:
257 struct Stats {
258 int tot_cleared = 0;
259 ID *last_id = nullptr;
260 };
261
262 Stats stats;
263};
264
266{
267 for (const PointerRNA &ptr : ids) {
269
270 ID *id = static_cast<ID *>(ptr.data);
271 if (!id->asset_data) {
272 continue;
273 }
274
275 if (!clear_id(id)) {
276 continue;
277 }
278
279 if (set_fake_user_) {
281 }
282
283 stats.tot_cleared++;
284 stats.last_id = id;
285 }
286}
287
289{
290 if (!wasSuccessful()) {
291 /* Dedicated error message for when there is an active asset detected, but it's not an ID local
292 * to this file. Helps users better understanding what's going on. */
293 if (AssetRepresentationHandle *active_asset = CTX_wm_asset(C); !active_asset->is_local_id()) {
295 RPT_ERROR,
296 "No asset data-blocks from the current file selected (assets must be stored in "
297 "the current file to be able to edit or clear them)");
298 }
299 else {
300 BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused");
301 }
302 }
303 else if (stats.tot_cleared == 1) {
304 /* If only one data-block: Give more useful message by printing asset name. */
306 &reports, RPT_INFO, "Data-block '%s' is not an asset anymore", stats.last_id->name + 2);
307 }
308 else {
309 BKE_reportf(&reports, RPT_INFO, "%i data-blocks are not assets anymore", stats.tot_cleared);
310 }
311}
312
314{
315 return stats.tot_cleared > 0;
316}
317
319 const wmOperator *op,
320 const Span<PointerRNA> ids)
321{
322 const bool set_fake_user = RNA_boolean_get(op->ptr, "set_fake_user");
323 AssetClearHelper clear_helper(set_fake_user);
324 clear_helper(ids);
325 clear_helper.reportResults(C, *op->reports);
326
327 if (!clear_helper.wasSuccessful()) {
328 return OPERATOR_CANCELLED;
329 }
330
333
334 return OPERATOR_FINISHED;
335}
336
338{
340
341 if (!ctx_stats.has_asset) {
342 const char *msg_single = N_("Data-block is not marked as asset");
343 const char *msg_multiple = N_("No data-block selected that is marked as asset");
344 CTX_wm_operator_poll_msg_set(C, ctx_stats.is_single ? msg_single : msg_multiple);
345 return false;
346 }
347 if (!ctx_stats.has_supported_type) {
349 return false;
350 }
351
352 return true;
353}
354
355static std::string asset_clear_get_description(bContext * /*C*/,
356 wmOperatorType * /*ot*/,
358{
359 const bool set_fake_user = RNA_boolean_get(ptr, "set_fake_user");
360 if (!set_fake_user) {
361 return "";
362 }
363 return TIP_(
364 "Delete all asset metadata, turning the selected asset data-blocks back into normal "
365 "data-blocks, and set Fake User to ensure the data-blocks will still be saved");
366}
367
372{
373 ot->name = "Clear Asset";
374 ot->description =
375 "Delete all asset metadata and turn the selected asset data-blocks back into normal "
376 "data-blocks";
377 ot->get_description = asset_clear_get_description;
378 ot->idname = "ASSET_OT_clear";
379
380 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
382 };
383 ot->poll = [](bContext *C) -> bool {
385 };
386
388
389 RNA_def_boolean(ot->srna,
390 "set_fake_user",
391 false,
392 "Set Fake User",
393 "Ensure the data-block is saved, even when it is no longer marked as asset");
394}
395
397{
398 ot->name = "Clear Single Asset";
399 ot->description =
400 "Delete all asset metadata and turn the asset data-block back into a normal data-block";
401 ot->get_description = asset_clear_get_description;
402 ot->idname = "ASSET_OT_clear_single";
403
404 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
406 };
407 ot->poll = [](bContext *C) -> bool {
409 };
410
412
413 RNA_def_boolean(ot->srna,
414 "set_fake_user",
415 false,
416 "Set Fake User",
417 "Ensure the data-block is saved, even when it is no longer marked as asset");
418}
419
420/* -------------------------------------------------------------------- */
421
423{
425 return true;
426 }
427
428 /* While not inside an Asset Browser, check if there's a asset list stored for the active asset
429 * library (stored in the workspace, obtained via context). */
431 if (!library) {
432 return false;
433 }
434
435 return list::has_list_storage_for_library(library) ||
437}
438
440{
442 /* Handles both global asset list storage and asset browsers. */
443 list::clear(library, C);
445
446 return OPERATOR_FINISHED;
447}
448
450{
451 /* identifiers */
452 ot->name = "Refresh Asset Library";
453 ot->description = "Reread assets and asset catalogs from the asset library on disk";
454 ot->idname = "ASSET_OT_library_refresh";
455
456 /* API callbacks. */
459}
460
461/* -------------------------------------------------------------------- */
462
464{
465 const SpaceFile *sfile = CTX_wm_space_file(C);
466 if (!sfile) {
467 return false;
468 }
470 if (!asset_library) {
471 return false;
472 }
473 if (catalogs_read_only(*asset_library)) {
474 CTX_wm_operator_poll_msg_set(C, "Asset catalogs cannot be edited in this asset library");
475 return false;
476 }
477 return true;
478}
479
481{
482 SpaceFile *sfile = CTX_wm_space_file(C);
484 char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr);
485
487 asset_library, DATA_("Catalog"), parent_path);
488
489 if (sfile) {
491 }
492
493 MEM_freeN(parent_path);
494
497
498 return OPERATOR_FINISHED;
499}
500
502{
503 /* identifiers */
504 ot->name = "New Asset Catalog";
505 ot->description = "Create a new catalog to put assets in";
506 ot->idname = "ASSET_OT_catalog_new";
507
508 /* API callbacks. */
511
512 RNA_def_string(ot->srna,
513 "parent_path",
514 nullptr,
515 0,
516 "Parent Path",
517 "Optional path defining the location to put the new catalog under");
518}
519
521{
522 SpaceFile *sfile = CTX_wm_space_file(C);
524 char *catalog_id_str = RNA_string_get_alloc(op->ptr, "catalog_id", nullptr, 0, nullptr);
525 asset_system::CatalogID catalog_id;
526 if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str)) {
527 return OPERATOR_CANCELLED;
528 }
529
530 catalog_remove(asset_library, catalog_id);
531
532 MEM_freeN(catalog_id_str);
533
536
537 return OPERATOR_FINISHED;
538}
539
541{
542 /* identifiers */
543 ot->name = "Delete Asset Catalog";
544 ot->description =
545 "Remove an asset catalog from the asset library (contained assets will not be affected and "
546 "show up as unassigned)";
547 ot->idname = "ASSET_OT_catalog_delete";
548
549 /* API callbacks. */
552
553 RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete");
554}
555
557{
558 const SpaceFile *sfile = CTX_wm_space_file(C);
559 if (!sfile || ED_fileselect_is_file_browser(sfile)) {
560 return nullptr;
561 }
562
564 if (asset_lib) {
565 return &asset_lib->catalog_service();
566 }
567
568 return nullptr;
569}
570
572{
574 if (!catalog_service) {
575 return OPERATOR_CANCELLED;
576 }
577
578 catalog_service->undo();
580 return OPERATOR_FINISHED;
581}
582
584{
586 return catalog_service && catalog_service->is_undo_possbile();
587}
588
590{
591 /* identifiers */
592 ot->name = "Undo Catalog Edits";
593 ot->description = "Undo the last edit to the asset catalogs";
594 ot->idname = "ASSET_OT_catalog_undo";
595
596 /* API callbacks. */
599}
600
602{
604 if (!catalog_service) {
605 return OPERATOR_CANCELLED;
606 }
607
608 catalog_service->redo();
610 return OPERATOR_FINISHED;
611}
612
614{
616 return catalog_service && catalog_service->is_redo_possbile();
617}
618
620{
621 /* identifiers */
622 ot->name = "Redo Catalog Edits";
623 ot->description = "Redo the last undone edit to the asset catalogs";
624 ot->idname = "ASSET_OT_catalog_redo";
625
626 /* API callbacks. */
629}
630
632{
634 if (!catalog_service) {
635 return OPERATOR_CANCELLED;
636 }
637
638 catalog_service->undo_push();
639 return OPERATOR_FINISHED;
640}
641
643{
644 return get_catalog_service(C) != nullptr;
645}
646
648{
649 /* identifiers */
650 ot->name = "Store undo snapshot for asset catalog edits";
651 ot->description = "Store the current state of the asset catalogs in the undo buffer";
652 ot->idname = "ASSET_OT_catalog_undo_push";
653
654 /* API callbacks. */
657
658 /* Generally artists don't need to find & use this operator, it's meant for scripts only. */
659 ot->flag = OPTYPE_INTERNAL;
660}
661
662/* -------------------------------------------------------------------- */
663
665{
667 return false;
668 }
669
670 const Main *bmain = CTX_data_main(C);
671 if (!bmain->filepath[0]) {
672 CTX_wm_operator_poll_msg_set(C, "Cannot save asset catalogs before the Blender file is saved");
673 return false;
674 }
675
677 CTX_wm_operator_poll_msg_set(C, "No changes to be saved");
678 return false;
679 }
680
681 return true;
682}
683
696
698{
699 /* identifiers */
700 ot->name = "Save Asset Catalogs";
701 ot->description =
702 "Make any edits to any catalogs permanent by writing the current set up to the asset "
703 "library";
704 ot->idname = "ASSET_OT_catalogs_save";
705
706 /* API callbacks. */
709}
710
711/* -------------------------------------------------------------------- */
712
713static bool could_be_asset_bundle(const Main *bmain);
715static bool is_contained_in_selected_asset_library(wmOperator *op, const char *filepath);
716static bool set_filepath_for_asset_lib(const Main *bmain, wmOperator *op);
717static bool has_external_files(Main *bmain, ReportList *reports);
718
720{
721 /* This operator only works when the asset browser is set to Current File. */
722 const SpaceFile *sfile = CTX_wm_space_file(C);
723 if (sfile == nullptr) {
724 return false;
725 }
727 return false;
728 }
729
730 const Main *bmain = CTX_data_main(C);
731 if (!could_be_asset_bundle(bmain)) {
732 return false;
733 }
734
735 /* Check whether this file is already located inside any asset library. */
737 &U, bmain->filepath);
738 if (asset_lib) {
739 return false;
740 }
741
742 return true;
743}
744
746 wmOperator *op,
747 const wmEvent * /*event*/)
748{
749 Main *bmain = CTX_data_main(C);
750 if (has_external_files(bmain, op->reports)) {
751 return OPERATOR_CANCELLED;
752 }
753
755
756 /* Make the "Save As" dialog box default to "${ASSET_LIB_ROOT}/${CURRENT_FILE}.blend". */
757 if (!set_filepath_for_asset_lib(bmain, op)) {
758 return OPERATOR_CANCELLED;
759 }
760
762}
763
765{
766 Main *bmain = CTX_data_main(C);
767 if (has_external_files(bmain, op->reports)) {
768 return OPERATOR_CANCELLED;
769 }
770
771 /* Check file path, copied from #wm_file_write(). */
772 char filepath[FILE_MAX];
773 RNA_string_get(op->ptr, "filepath", filepath);
774 const size_t len = strlen(filepath);
775
776 if (len == 0) {
777 BKE_report(op->reports, RPT_ERROR, "Path is empty, cannot save");
778 return OPERATOR_CANCELLED;
779 }
780
781 if (len >= FILE_MAX) {
782 BKE_report(op->reports, RPT_ERROR, "Path too long, cannot save");
783 return OPERATOR_CANCELLED;
784 }
785
786 /* Check that the destination is actually contained in the selected asset library. */
787 if (!is_contained_in_selected_asset_library(op, filepath)) {
788 BKE_reportf(op->reports, RPT_ERROR, "Selected path is outside of the selected asset library");
789 return OPERATOR_CANCELLED;
790 }
791
792 WM_cursor_wait(true);
794 /* Store undo step, such that on a failed save the 'prepare_to_merge_on_write' call can be
795 * un-done. */
796 cat_service->undo_push();
797 cat_service->prepare_to_merge_on_write();
798
799 const wmOperatorStatus operator_result = WM_operator_name_call(
800 C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, op->ptr, nullptr);
801 WM_cursor_wait(false);
802
803 if (operator_result != OPERATOR_FINISHED) {
804 cat_service->undo();
805 return operator_result;
806 }
807
809 BLI_assert_msg(lib, "If the asset library is not known, how did we get here?");
811 RPT_INFO,
812 R"(Saved "%s" to asset library "%s")",
814 lib->name);
815 return OPERATOR_FINISHED;
816}
817
819 PointerRNA * /*ptr*/,
820 PropertyRNA * /*prop*/,
821 bool *r_free)
822{
824 if (!items) {
825 *r_free = false;
826 return nullptr;
827 }
828
829 *r_free = true;
830 return items;
831}
832
834{
835 /* identifiers */
836 ot->name = "Copy to Asset Library";
837 ot->description =
838 "Copy the current .blend file into an Asset Library. Only works on standalone .blend files "
839 "(i.e. when no other files are referenced)";
840 ot->idname = "ASSET_OT_bundle_install";
841
842 /* API callbacks. */
846
847 ot->prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE);
850
854 FILE_SAVE,
858}
859
860/* Cheap check to see if this is an "asset bundle" just by checking main file name.
861 * A proper check will be done in the exec function, to ensure that no external files will be
862 * referenced. */
863static bool could_be_asset_bundle(const Main *bmain)
864{
865 return fnmatch("*_bundle.blend", bmain->filepath, FNM_CASEFOLD) == 0;
866}
867
869{
870 const int enum_value = RNA_enum_get(op->ptr, "asset_library_reference");
873 &U, lib_ref.custom_library_index);
874 return lib;
875}
876
877static bool is_contained_in_selected_asset_library(wmOperator *op, const char *filepath)
878{
880 if (!lib) {
881 return false;
882 }
883 return BLI_path_contains(lib->dirpath, filepath);
884}
885
890static bool set_filepath_for_asset_lib(const Main *bmain, wmOperator *op)
891{
892 /* Find the directory path of the selected asset library. */
894 if (lib == nullptr) {
895 return false;
896 }
897
898 /* Concatenate the filename of the current blend file. */
899 const char *blend_filename = BLI_path_basename(bmain->filepath);
900 if (blend_filename == nullptr || blend_filename[0] == '\0') {
901 return false;
902 }
903
904 char file_path[FILE_MAX];
905 BLI_path_join(file_path, sizeof(file_path), lib->dirpath, blend_filename);
906 RNA_string_set(op->ptr, "filepath", file_path);
907
908 return true;
909}
910
915
917 char * /*path_dst*/,
918 size_t /*path_dst_maxncpy*/,
919 const char *path_src)
920{
921 FileCheckCallbackInfo *callback_info = static_cast<FileCheckCallbackInfo *>(
922 bpath_data->user_data);
923 callback_info->external_files.add(std::string(path_src));
924 return false;
925}
926
935{
937
939 (BKE_BPATH_FOREACH_PATH_SKIP_PACKED /* Packed files are fine. */
940 | BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE /* Only report multi-files once, it's enough. */
941 | BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES); /* Only care about actually used files. */
942
943 BPathForeachPathData bpath_data = {
944 /*bmain*/ bmain,
945 /*callback_function*/ &external_file_check_callback,
946 /*flag*/ flag,
947 /*user_data*/ &callback_info,
948 /*absolute_base_path*/ nullptr,
949 };
950 BKE_bpath_foreach_path_main(&bpath_data);
951
952 if (callback_info.external_files.is_empty()) {
953 /* No external dependencies. */
954 return false;
955 }
956
957 if (callback_info.external_files.size() == 1) {
958 /* Only one external dependency, report it directly. */
959 BKE_reportf(callback_info.reports,
960 RPT_ERROR,
961 "Unable to copy bundle due to external dependency: \"%s\"",
962 callback_info.external_files.begin()->c_str());
963 return true;
964 }
965
966 /* Multiple external dependencies, report the aggregate and put details on console. */
968 callback_info.reports,
969 RPT_ERROR,
970 "Unable to copy bundle due to %zu external dependencies; more details on the console",
971 size_t(callback_info.external_files.size()));
972 printf("Unable to copy bundle due to %zu external dependencies:\n",
973 size_t(callback_info.external_files.size()));
974 for (const std::string &path : callback_info.external_files) {
975 printf(" \"%s\"\n", path.c_str());
976 }
977 return true;
978}
979
980constexpr int DRAG_THRESHOLD = 4;
981
985 /* Screenshot points may not be set immediately to allow for clicking to create a screenshot with
986 * the previous size. */
988
990 /* Dragged far enough to create the screenshot are instead of registering as a click. */
992 /* Move the whole screenshot area when moving the cursor instead of placing `drag_end`. */
995};
996
997/* Sort points so p1 is lower left, and p2 is top right. */
998static inline void sort_points(int2 &p1, int2 &p2)
999{
1000 if (p1.x > p2.x) {
1001 std::swap(p1.x, p2.x);
1002 }
1003 if (p1.y > p2.y) {
1004 std::swap(p1.y, p2.y);
1005 }
1006}
1007
1008/* Clamps the point to the window bounds. */
1009static inline int2 clamp_point_to_window(const int2 &point, const wmWindow *window)
1010{
1011 const int2 win_size = WM_window_native_pixel_size(window);
1012 return {clamp_i(point.x, 0, win_size.x - 1), clamp_i(point.y, 0, win_size.y - 1)};
1013}
1014
1015/* Ensures that the x and y distance to from p1 to p2 is equal and the resulting square remains
1016 * fully within the window bounds. The two points can be in any spacial relation to each other i.e.
1017 * if p1 was top left, it remains top left. */
1018static inline void square_points_clamp_to_window(const int2 &p1, int2 &p2, const wmWindow *window)
1019{
1020 const int2 delta = p2 - p1;
1021
1022 /* Determine the drag direction for each axis. */
1023 const int dir_x = (delta.x >= 0) ? 1 : -1;
1024 const int dir_y = (delta.y >= 0) ? 1 : -1;
1025
1026 const int size_x = std::abs(delta.x);
1027 const int size_y = std::abs(delta.y);
1028 int square_size = std::max(size_x, size_y);
1029
1030 /* Compute maximum size that fits within window bounds in the drag direction. */
1031 const int2 win_size = WM_window_native_pixel_size(window);
1032 const int max_size_x = (dir_x > 0) ? win_size.x - p1.x - 1 : p1.x;
1033 const int max_size_y = (dir_y > 0) ? win_size.y - p1.y - 1 : p1.y;
1034
1035 /* Clamp the square size so it does not exceed window bounds. */
1036 square_size = std::min(square_size, std::min(max_size_x, max_size_y));
1037
1038 /* Update p2 to form a clamped square in the same direction as the drag. */
1039 p2.x = p1.x + dir_x * square_size;
1040 p2.y = p1.y + dir_y * square_size;
1041}
1042
1043static void generate_previewimg_from_buffer(ID *id, const ImBuf *image_buffer)
1044{
1045 PreviewImage *preview_image = BKE_previewimg_id_ensure(id);
1046 BKE_previewimg_clear(preview_image);
1047
1048 for (int size_type = 0; size_type < NUM_ICON_SIZES; size_type++) {
1049 BKE_previewimg_ensure(preview_image, size_type);
1050 int width = image_buffer->x;
1051 int height = image_buffer->y;
1052 int max_size = 0;
1053 switch (size_type) {
1054 case ICON_SIZE_ICON:
1055 max_size = ICON_RENDER_DEFAULT_HEIGHT;
1056 break;
1057 case ICON_SIZE_PREVIEW:
1058 max_size = PREVIEW_RENDER_LARGE_HEIGHT;
1059 break;
1060 }
1061 if (max_size == 0) {
1062 /* Can only be reached if a new icon size is added. */
1064 continue;
1065 }
1066
1067 /* Scales down the image to `max_size` while maintaining the
1068 * aspect ratio. */
1069 if (image_buffer->x > image_buffer->y) {
1070 width = max_size;
1071 height = image_buffer->y * (width / float(image_buffer->x));
1072 }
1073 else if (image_buffer->y > image_buffer->x) {
1074 height = max_size;
1075 width = image_buffer->x * (height / float(image_buffer->y));
1076 }
1077 else {
1078 width = height = max_size;
1079 }
1080
1081 ImBuf *scaled_imbuf = IMB_scale_into_new(
1082 image_buffer, width, height, IMBScaleFilter::Nearest, false);
1083 preview_image->rect[size_type] = (uint *)MEM_dupallocN(scaled_imbuf->byte_buffer.data);
1084 preview_image->w[size_type] = width;
1085 preview_image->h[size_type] = height;
1086 preview_image->flag[size_type] |= PRV_USER_EDITED;
1087 IMB_freeImBuf(scaled_imbuf);
1088 }
1089}
1090
1095static ImBuf *take_screenshot_crop(bContext *C, const rcti &crop_rect)
1096{
1097 int dumprect_size[2];
1098 wmWindow *win = CTX_wm_window(C);
1099 uint8_t *dumprect = WM_window_pixels_read(C, win, dumprect_size);
1100
1101 /* Clamp coordinates to window bounds. */
1102 rcti safe_rect = crop_rect;
1103 safe_rect.xmin = max_ii(0, crop_rect.xmin);
1104 safe_rect.ymin = max_ii(0, crop_rect.ymin);
1105 safe_rect.xmax = min_ii(dumprect_size[0] - 1, crop_rect.xmax);
1106 safe_rect.ymax = min_ii(dumprect_size[1] - 1, crop_rect.ymax);
1107
1108 /* Validate rectangle. */
1109 if (!BLI_rcti_is_valid(&safe_rect)) {
1110 MEM_freeN(dumprect);
1111 return nullptr;
1112 }
1113
1114 ImBuf *image_buffer = IMB_allocImBuf(dumprect_size[0], dumprect_size[1], 24, 0);
1115 /* Using IB_TAKE_OWNERSHIP because the crop does kind of take ownership already it seems. At
1116 * least freeing the memory after would cause a crash if ownership isn't taken. */
1117 IMB_assign_byte_buffer(image_buffer, dumprect, IB_TAKE_OWNERSHIP);
1118
1119 IMB_rect_crop(image_buffer, &safe_rect);
1120 return image_buffer;
1121}
1122
1124{
1125 int2 p1, p2;
1126 wmWindow *win = CTX_wm_window(C);
1127 RNA_int_get_array(op->ptr, "p1", p1);
1128 RNA_int_get_array(op->ptr, "p2", p2);
1129
1130 /* Clamp points to window bounds, so the screenshot area is always valid. */
1131 p1 = clamp_point_to_window(p1, win);
1132 p2 = clamp_point_to_window(p2, win);
1133
1134 /* Squaring has to happen before sorting so the area is squared from the point where
1135 * dragging started. */
1136 if (RNA_boolean_get(op->ptr, "force_square")) {
1137 square_points_clamp_to_window(p1, p2, win);
1138 }
1139
1140 sort_points(p1, p2);
1141
1142 /* The min side is chosen arbitrarily to avoid accidental creations of very small screenshots. */
1143 constexpr int min_side = 16;
1144 if (p2.x - p1.x < min_side || p2.y - p1.y < min_side) {
1146 op->reports, RPT_ERROR, "Screenshot cannot be smaller than %i pixels on a side", min_side);
1147 return OPERATOR_CANCELLED;
1148 }
1149
1150 ImBuf *image_buffer;
1151
1154 /* Special case for taking a screenshot from a 3D viewport. In that case we do an offscreen
1155 * render to support transparency. Render settings are used as currently set up in the viewport
1156 * to comply with WYSIWYG as much as possible. One limitation is that GUI elements will not be
1157 * visible in the render. */
1158 bool render_offscreen = false;
1159 if (area_p1 == area_p2 && area_p1 != nullptr && area_p1->spacetype == SPACE_VIEW3D) {
1160 Scene *scene = CTX_data_scene(C);
1161 View3D *v3d = static_cast<View3D *>(area_p1->spacedata.first);
1162 /* For `ED_view3d_draw_offscreen_imbuf` only EEVEE only produces a good result. See #141732. */
1163 if (eDrawType(v3d->shading.type) == OB_RENDER) {
1164 const char *engine_name = scene->r.engine;
1165 render_offscreen = STR_ELEM(engine_name,
1169 }
1170 else {
1171 render_offscreen = true;
1172 }
1173 }
1174 if (render_offscreen) {
1175 View3D *v3d = static_cast<View3D *>(area_p1->spacedata.first);
1177 if (!region) {
1178 /* Unlikely to be hit, but just being cautious. */
1180 return OPERATOR_CANCELLED;
1181 }
1182 char err_out[256] = "unknown";
1185 eDrawType(v3d->shading.type),
1186 v3d,
1187 region,
1188 region->winx,
1189 region->winy,
1192 nullptr,
1193 false,
1194 nullptr,
1195 nullptr,
1196 err_out);
1197
1198 /* Convert crop rect into the space relative to the area. */
1199 const rcti crop_rect = {p1.x - area_p1->totrct.xmin,
1200 p2.x - area_p1->totrct.xmin,
1201 p1.y - area_p1->totrct.ymin,
1202 p2.y - area_p1->totrct.ymin};
1203 IMB_rect_crop(image_buffer, &crop_rect);
1204 }
1205 else {
1206 const rcti crop_rect = {p1.x, p2.x, p1.y, p2.y};
1207 image_buffer = take_screenshot_crop(C, crop_rect);
1208 if (!image_buffer) {
1209 BKE_report(op->reports, RPT_ERROR, "Invalid screenshot area selection");
1210 return OPERATOR_CANCELLED;
1211 }
1212 }
1213
1214 const AssetRepresentationHandle *asset_handle = CTX_wm_asset(C);
1215 BLI_assert_msg(asset_handle != nullptr, "This is ensured by poll");
1216 AssetWeakReference asset_reference = asset_handle->make_weak_reference();
1217
1218 Main *bmain = CTX_data_main(C);
1220 *bmain, asset_handle->get_id_type(), asset_reference);
1221 BLI_assert(id != nullptr);
1222
1224
1225 generate_previewimg_from_buffer(id, image_buffer);
1226 IMB_freeImBuf(image_buffer);
1227
1229 const bool saved = bke::asset_edit_id_save(*bmain, *id, *op->reports);
1230 if (!saved) {
1231 BKE_report(op->reports, RPT_ERROR, "Saving failed");
1232 }
1233 }
1234
1237
1239
1240 return OPERATOR_FINISHED;
1241}
1242
1243static void screenshot_preview_draw(const wmWindow *window, void *operator_data)
1244{
1245 ScreenshotOperatorData *data = static_cast<ScreenshotOperatorData *>(operator_data);
1246 int2 p1 = data->p1;
1247 int2 p2 = data->p2;
1248
1249 /* Clamp points to window bounds, so the screenshot area is always valid. */
1250 p1 = clamp_point_to_window(p1, window);
1251 p2 = clamp_point_to_window(p2, window);
1252
1253 /* Squaring has to happen before sorting so the area is squared from the point where
1254 * dragging started. */
1255 if (data->force_square) {
1256 square_points_clamp_to_window(p1, p2, window);
1257 }
1258
1259 sort_points(p1, p2);
1260
1261 /* Drawing rect just out of the screenshot area to not capture the box in the picture. */
1262 const rctf screenshot_rect = {
1263 float(p1.x - 1), float(p2.x + 1), float(p1.y - 1), float(p2.y + 1)};
1264
1265 /* Drawing a semi-transparent mask to highlight the area that will be captured. */
1266 float4 mask_color = {1, 1, 1, 0.25};
1267 const int2 win_size = WM_window_native_pixel_size(window);
1268 const rctf mask_rect_bottom = {0, float(win_size.x), 0, screenshot_rect.ymin};
1269 UI_draw_roundbox_aa(&mask_rect_bottom, true, 0, mask_color);
1270 const rctf mask_rect_top = {0, float(win_size.x), screenshot_rect.ymax, float(win_size.y)};
1271 UI_draw_roundbox_aa(&mask_rect_top, true, 0, mask_color);
1272 const rctf mask_rect_left = {
1273 0, screenshot_rect.xmin, screenshot_rect.ymin, screenshot_rect.ymax};
1274 UI_draw_roundbox_aa(&mask_rect_left, true, 0, mask_color);
1275 const rctf mask_rect_right = {
1276 screenshot_rect.xmax, float(win_size.x), screenshot_rect.ymin, screenshot_rect.ymax};
1277 UI_draw_roundbox_aa(&mask_rect_right, true, 0, mask_color);
1278
1279 float4 color;
1281 UI_draw_roundbox_aa(&screenshot_rect, false, 0, color);
1282}
1283
1285{
1286 wmWindow *win = CTX_wm_window(C);
1289 WM_draw_cb_exit(win, data->draw_handle);
1290 MEM_freeN(data);
1291 ED_workspace_status_text(C, nullptr);
1292}
1293
1295{
1296 RNA_boolean_set(op->ptr, "force_square", data->force_square);
1297 RNA_int_set_array(op->ptr, "p1", data->p1);
1298 RNA_int_set_array(op->ptr, "p2", data->p2);
1299}
1300
1302{
1303 ARegion *region = CTX_wm_region(C);
1304 wmWindow *win = CTX_wm_window(C);
1306
1307 const int2 screen_space_cursor = {
1308 event->mval[0] + region->winrct.xmin,
1309 event->mval[1] + region->winrct.ymin,
1310 };
1311 switch (event->type) {
1312 case LEFTMOUSE: {
1313 switch (event->val) {
1314 case KM_PRESS:
1315 data->is_mouse_down = true;
1316 data->crossed_threshold = false;
1317 data->drag_start = screen_space_cursor;
1318 break;
1319 case KM_RELEASE:
1320 data->is_mouse_down = false;
1321 data->drag_end = clamp_point_to_window(screen_space_cursor, win);
1325 return OPERATOR_FINISHED;
1326 }
1327 break;
1328 }
1329
1330 case EVT_PADENTER:
1331 case EVT_RETKEY: {
1335 return OPERATOR_FINISHED;
1336 }
1337
1338 case RIGHTMOUSE:
1339 case EVT_ESCKEY: {
1341 CTX_wm_screen(C)->do_draw = true;
1342 return OPERATOR_CANCELLED;
1343 }
1344
1345 case EVT_SPACEKEY: {
1346 switch (event->val) {
1347 case KM_PRESS:
1348 data->shift_area = true;
1349 break;
1350 case KM_RELEASE:
1351 data->shift_area = false;
1352 break;
1353
1354 default:
1355 break;
1356 }
1357 break;
1358 }
1359
1360 case EVT_LEFTSHIFTKEY:
1361 case EVT_RIGHTSHIFTKEY: {
1362 switch (event->val) {
1363 case KM_PRESS:
1364 data->force_square = false;
1365 break;
1366 case KM_RELEASE:
1367 data->force_square = true;
1368 break;
1369
1370 default:
1371 break;
1372 }
1373 break;
1374 }
1375
1376 case MOUSEMOVE: {
1377 if (data->shift_area) {
1378 const int2 delta = screen_space_cursor - data->last_cursor;
1379 const int2 new_p1 = data->p1 + delta;
1380 const int2 new_p2 = data->p2 + delta;
1381
1382 auto is_within_window = [win](const int2 &pt) -> bool {
1383 const int2 win_size = WM_window_native_pixel_size(win);
1384 return pt.x >= 0 && pt.x < win_size.x && pt.y >= 0 && pt.y < win_size.y;
1385 };
1386
1387 /* Apply movement only if the entire rectangle stays within window bounds. */
1388 if (is_within_window(new_p1) && is_within_window(new_p2)) {
1389 data->p1 = new_p1;
1390 data->p2 = new_p2;
1391 }
1392 }
1393 else if (data->is_mouse_down) {
1394 data->drag_end = clamp_point_to_window(screen_space_cursor, win);
1395
1396 if (!data->crossed_threshold) {
1397 const int2 delta = data->drag_end - data->drag_start;
1398 if (std::abs(delta.x) > DRAG_THRESHOLD && std::abs(delta.y) > DRAG_THRESHOLD) {
1399 /* Only set the points once the threshold has been crossed. This allows to just
1400 * click to confirm using a potentially existing screenshot rect. */
1401 data->crossed_threshold = true;
1402 data->p1 = data->drag_start;
1403 }
1404 }
1405
1406 if (data->crossed_threshold) {
1407 data->p2 = data->drag_end;
1408 }
1409 }
1410
1411 CTX_wm_screen(C)->do_draw = true;
1412 data->last_cursor = screen_space_cursor;
1413 break;
1414 }
1415
1416 default:
1417 break;
1418 }
1419
1420 WorkspaceStatus status(C);
1421 if (data->is_mouse_down) {
1422 status.item(IFACE_("Cancel"), ICON_EVENT_ESC, ICON_MOUSE_RMB);
1423 }
1424 else {
1425 status.item(IFACE_("Start"), ICON_MOUSE_LMB_DRAG);
1426 }
1427 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB, ICON_EVENT_RETURN);
1428 status.item(IFACE_("Move"), ICON_EVENT_SPACEKEY);
1429 status.item(IFACE_("Unlock Aspect Ratio"), ICON_EVENT_SHIFT);
1430
1432}
1433
1435 wmOperator *op,
1436 const wmEvent * /* event */)
1437{
1438 wmWindow *win = CTX_wm_window(C);
1440
1441 op->customdata = MEM_callocN(sizeof(ScreenshotOperatorData), __func__);
1444 data->is_mouse_down = false;
1445 RNA_int_get_array(op->ptr, "p1", data->p1);
1446 RNA_int_get_array(op->ptr, "p2", data->p2);
1447 data->last_cursor = data->p1;
1448 data->shift_area = false;
1449 data->crossed_threshold = false;
1450 data->force_square = RNA_boolean_get(op->ptr, "force_square");
1451
1453 CTX_wm_screen(C)->do_draw = true;
1454
1456}
1457
1459{
1460 if (G.background) {
1461 return false;
1462 }
1463
1464 const AssetRepresentationHandle *asset_handle = CTX_wm_asset(C);
1465 if (!asset_handle) {
1466 CTX_wm_operator_poll_msg_set(C, "No selected asset");
1467 return false;
1468 }
1469 if (asset_handle->is_local_id()) {
1470 return WM_operator_winactive(C);
1471 }
1472
1473 std::string lib_path = asset_handle->full_library_path();
1474 if (StringRef(lib_path).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
1475 return true;
1476 }
1477
1478 CTX_wm_operator_poll_msg_set(C, "Asset cannot be modified from this file");
1479 return false;
1480}
1481
1483{
1484 /* This should be a generic operator for assets not linked to the pose-library. */
1485
1486 ot->name = "Capture Screenshot Preview";
1487 ot->description = "Capture a screenshot to use as a preview for the selected asset";
1488 ot->idname = "ASSET_OT_screenshot_preview";
1489
1491 ot->invoke = screenshot_preview_invoke;
1494
1495 RNA_def_int_array(ot->srna,
1496 "p1",
1497 2,
1498 nullptr,
1499 0,
1500 INT_MAX,
1501 "Point 1",
1502 "First point of the screenshot in screenspace",
1503 0,
1504 3840);
1505 RNA_def_int_array(ot->srna,
1506 "p2",
1507 2,
1508 nullptr,
1509 0,
1510 INT_MAX,
1511 "Point 2",
1512 "Second point of the screenshot in screenspace",
1513 0,
1514 3840);
1515 RNA_def_boolean(ot->srna,
1516 "force_square",
1517 true,
1518 "Force Square",
1519 "If enabled, the screenshot will have the same height as width");
1520}
1521
1522/* -------------------------------------------------------------------- */
1523
1543
1544} // namespace blender::ed::asset
bool AS_asset_library_has_any_unsaved_catalogs()
Main runtime representation of an asset.
#define BLENDER_ASSET_FILE_SUFFIX
eBPathForeachFlag
Definition BKE_bpath.hh:27
@ BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES
Definition BKE_bpath.hh:47
@ BKE_BPATH_FOREACH_PATH_SKIP_PACKED
Definition BKE_bpath.hh:37
@ BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE
Definition BKE_bpath.hh:61
void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data)
Definition bpath.cc:116
bScreen * CTX_wm_screen(const bContext *C)
SpaceFile * CTX_wm_space_file(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
const AssetLibraryReference * CTX_wm_asset_library_ref(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
class blender::asset_system::AssetRepresentation * CTX_wm_asset(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define ICON_RENDER_DEFAULT_HEIGHT
Definition BKE_icons.h:149
void id_fake_user_set(ID *id)
Definition lib_id.cc:391
struct bUserAssetLibrary * BKE_preferences_asset_library_find_index(const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
struct bUserAssetLibrary * BKE_preferences_asset_library_containing_path(const struct UserDef *userdef, const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
void BKE_previewimg_ensure(PreviewImage *prv, int size)
PreviewImage * BKE_previewimg_id_ensure(ID *id)
void BKE_previewimg_clear(PreviewImage *prv)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:840
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE int clamp_i(int value, int min, int max)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAX
bool BLI_path_contains(const char *container_path, const char *containee_path) ATTR_NONNULL(1
#define BLI_path_join(...)
bool BLI_rcti_is_valid(const struct rcti *rect)
#define STR_ELEM(...)
Definition BLI_string.h:656
unsigned int uint
bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) ATTR_NONNULL()
Definition uuid.cc:112
#define TIP_(msgid)
#define IFACE_(msgid)
#define DATA_(msgid)
@ PRV_USER_EDITED
Definition DNA_ID.h:543
@ ICON_SIZE_PREVIEW
@ ICON_SIZE_ICON
@ NUM_ICON_SIZES
eDrawType
@ OB_RENDER
@ R_ALPHAPREMUL
@ RGN_TYPE_WINDOW
@ FILE_SORT_DEFAULT
@ FILE_BLENDER
@ FILE_TYPE_BLENDER
@ FILE_TYPE_FOLDER
@ SPACE_VIEW3D
@ FILE_DEFAULTDISPLAY
#define SPACE_TYPE_ANY
struct AssetRepresentationHandle AssetRepresentationHandle
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING
void ED_fileselect_activate_asset_catalog(const SpaceFile *sfile, bUUID catalog_id)
Definition filesel.cc:503
bool ED_fileselect_is_file_browser(const SpaceFile *sfile)
Definition filesel.cc:465
bool ED_fileselect_is_local_asset_library(const SpaceFile *sfile)
Definition filesel.cc:410
blender::asset_system::AssetLibrary * ED_fileselect_active_asset_library_get(const SpaceFile *sfile)
Definition filesel.cc:475
void ED_preview_kill_jobs_for_id(wmWindowManager *wm, const ID *id)
ScrArea * ED_area_find_under_cursor(const bContext *C, int spacetype, const int event_xy[2])
Definition area.cc:3802
bool ED_operator_asset_browsing_active(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1040
blender::Vector< PointerRNA > ED_operator_single_id_from_context_as_vec(const bContext *C)
blender::Vector< PointerRNA > ED_operator_get_ids_from_context_as_vec(const bContext *C)
ImBuf * ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, Scene *scene, eDrawType drawtype, View3D *v3d, ARegion *region, int sizex, int sizey, eImBufFlags imbuf_flag, int alpha_mode, const char *viewname, bool restore_rv3d_mats, GPUOffScreen *ofs, GPUViewport *viewport, char err_out[256])
void IMB_rect_crop(ImBuf *ibuf, const rcti *crop)
Definition rectop.cc:242
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
ImBuf * IMB_scale_into_new(const ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:801
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership)
@ IB_TAKE_OWNERSHIP
@ IB_byte_data
#define PREVIEW_RENDER_LARGE_HEIGHT
Definition IMB_thumbs.hh:41
@ PROP_ENUM
Definition RNA_types.hh:154
@ PROP_HIDDEN
Definition RNA_types.hh:324
@ PROP_NONE
Definition RNA_types.hh:221
#define C
Definition RandGen.cpp:29
void UI_draw_roundbox_aa(const rctf *rect, bool filled, float rad, const float color[4])
@ TH_EDITOR_BORDER
void UI_GetThemeColor4fv(int colorid, float col[4])
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:1075
@ FILE_SAVE
Definition WM_api.hh:1085
#define ND_ASSET_LIST_READING
Definition WM_types.hh:547
#define NC_ID
Definition WM_types.hh:392
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SPACE_ASSET_PARAMS
Definition WM_types.hh:522
#define NA_ADDED
Definition WM_types.hh:583
#define NA_EDITED
Definition WM_types.hh:581
ReportList * reports
Definition WM_types.hh:1025
#define NC_ASSET
Definition WM_types.hh:401
#define NA_REMOVED
Definition WM_types.hh:584
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:245
#define ND_ASSET_LIST
Definition WM_types.hh:545
#define ND_ASSET_CATALOGS
Definition WM_types.hh:551
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
#define NC_SPACE
Definition WM_types.hh:389
#define U
BMesh const char void * data
int size_type
void item(std::string text, int icon1, int icon2=0)
Definition area.cc:979
Iterator begin() const
Definition BLI_set.hh:480
int64_t size() const
Definition BLI_set.hh:587
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_set.hh:595
constexpr int64_t size() const
Definition BLI_span.hh:252
AssetCatalogService & catalog_service() const
void reportResults(const bContext *C, ReportList &reports) const
Definition asset_ops.cc:288
void operator()(Span< PointerRNA > ids)
Definition asset_ops.cc:265
AssetClearHelper(const bool set_fake_user)
Definition asset_ops.cc:249
void operator()(const bContext &C, Span< PointerRNA > ids)
Definition asset_ops.cc:124
void reportResults(ReportList &reports) const
Definition asset_ops.cc:149
#define printf(...)
#define ID_IS_ASSET(_id)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
ID * asset_edit_id_from_weak_reference(Main &global_main, ID_Type id_type, const AssetWeakReference &weak_ref)
bool asset_edit_id_is_writable(const ID &id)
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports)
bool has_asset_browser_storage_for_library(const AssetLibraryReference *library_reference, const bContext *C)
void clear(const AssetLibraryReference *library_reference, const bContext *C)
bool has_list_storage_for_library(const AssetLibraryReference *library_reference)
static bool asset_catalogs_save_poll(bContext *C)
Definition asset_ops.cc:664
static bool could_be_asset_bundle(const Main *bmain)
Definition asset_ops.cc:863
void catalog_remove(asset_system::AssetLibrary *library, const asset_system::CatalogID &catalog_id)
static void generate_previewimg_from_buffer(ID *id, const ImBuf *image_buffer)
bool catalogs_read_only(const asset_system::AssetLibrary &library)
static bool asset_catalog_undo_push_poll(bContext *C)
Definition asset_ops.cc:642
static bool external_file_check_callback(BPathForeachPathData *bpath_data, char *, size_t, const char *path_src)
Definition asset_ops.cc:916
static void ASSET_OT_clear(wmOperatorType *ot)
Definition asset_ops.cc:371
static wmOperatorStatus asset_catalogs_save_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:684
void catalogs_save_from_main_path(asset_system::AssetLibrary *library, const Main *bmain)
static void ASSET_OT_catalog_redo(wmOperatorType *ot)
Definition asset_ops.cc:619
AssetLibraryReference library_reference_from_enum_value(int value)
static std::string asset_clear_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition asset_ops.cc:355
static void ASSET_OT_catalog_new(wmOperatorType *ot)
Definition asset_ops.cc:501
static bool asset_catalog_redo_poll(bContext *C)
Definition asset_ops.cc:613
static void ASSET_OT_clear_single(wmOperatorType *ot)
Definition asset_ops.cc:396
static void ASSET_OT_catalog_delete(wmOperatorType *ot)
Definition asset_ops.cc:540
static wmOperatorStatus asset_bundle_install_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition asset_ops.cc:745
static IDVecStats asset_operation_get_id_vec_stats_from_ids(const Span< PointerRNA > id_pointers)
Definition asset_ops.cc:73
asset_system::AssetCatalog * catalog_add(asset_system::AssetLibrary *library, StringRefNull name, StringRef parent_path=nullptr)
static void ASSET_OT_mark_single(wmOperatorType *ot)
Definition asset_ops.cc:225
static bool is_contained_in_selected_asset_library(wmOperator *op, const char *filepath)
Definition asset_ops.cc:877
static bool asset_clear_poll(bContext *C, const Span< PointerRNA > ids)
Definition asset_ops.cc:337
static int2 clamp_point_to_window(const int2 &point, const wmWindow *window)
static wmOperatorStatus asset_catalog_new_exec(bContext *C, wmOperator *op)
Definition asset_ops.cc:480
static bool asset_bundle_install_poll(bContext *C)
Definition asset_ops.cc:719
static void screenshot_preview_draw(const wmWindow *window, void *operator_data)
const EnumPropertyItem * custom_libraries_rna_enum_itemf()
static void ASSET_OT_catalog_undo_push(wmOperatorType *ot)
Definition asset_ops.cc:647
static bool set_filepath_for_asset_lib(const Main *bmain, wmOperator *op)
Definition asset_ops.cc:890
static wmOperatorStatus screenshot_preview_exec(bContext *C, wmOperator *op)
static wmOperatorStatus asset_catalog_redo_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:601
static ImBuf * take_screenshot_crop(bContext *C, const rcti &crop_rect)
static wmOperatorStatus screenshot_preview_modal(bContext *C, wmOperator *op, const wmEvent *event)
void generate_preview(const bContext *C, ID *id)
constexpr int DRAG_THRESHOLD
Definition asset_ops.cc:980
static void ASSET_OT_bundle_install(wmOperatorType *ot)
Definition asset_ops.cc:833
static bool screenshot_preview_poll(bContext *C)
static const char * asset_operation_unsupported_type_msg(const bool is_single)
Definition asset_ops.cc:94
static bool asset_catalog_undo_poll(bContext *C)
Definition asset_ops.cc:583
static wmOperatorStatus asset_library_refresh_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:439
static void screenshot_preview_exit(bContext *C, wmOperator *op)
static bool asset_mark_poll(bContext *C, const Span< PointerRNA > ids)
Definition asset_ops.cc:192
static void ASSET_OT_mark(wmOperatorType *ot)
Definition asset_ops.cc:204
static wmOperatorStatus asset_catalog_delete_exec(bContext *C, wmOperator *op)
Definition asset_ops.cc:520
static void ASSET_OT_catalog_undo(wmOperatorType *ot)
Definition asset_ops.cc:589
static bool asset_library_refresh_poll(bContext *C)
Definition asset_ops.cc:422
void refresh_asset_library_from_asset(const bContext *C, const blender::asset_system::AssetRepresentation &asset)
static asset_system::AssetCatalogService * get_catalog_service(bContext *C)
Definition asset_ops.cc:556
static void ASSET_OT_screenshot_preview(wmOperatorType *ot)
static void sort_points(int2 &p1, int2 &p2)
Definition asset_ops.cc:998
static wmOperatorStatus asset_catalog_undo_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:571
static wmOperatorStatus screenshot_preview_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void square_points_clamp_to_window(const int2 &p1, int2 &p2, const wmWindow *window)
static const bUserAssetLibrary * selected_asset_library(wmOperator *op)
Definition asset_ops.cc:868
static wmOperatorStatus asset_clear_exec(const bContext *C, const wmOperator *op, const Span< PointerRNA > ids)
Definition asset_ops.cc:318
static wmOperatorStatus asset_bundle_install_exec(bContext *C, wmOperator *op)
Definition asset_ops.cc:764
static void screenshot_area_transfer_to_rna(wmOperator *op, ScreenshotOperatorData *data)
static wmOperatorStatus asset_mark_exec(const bContext *C, const wmOperator *op, const Span< PointerRNA > ids)
Definition asset_ops.cc:174
static void ASSET_OT_library_refresh(wmOperatorType *ot)
Definition asset_ops.cc:449
bool id_type_is_supported(const ID *id)
Definition asset_type.cc:26
static void ASSET_OT_catalogs_save(wmOperatorType *ot)
Definition asset_ops.cc:697
static bool has_external_files(Main *bmain, ReportList *reports)
Definition asset_ops.cc:934
static wmOperatorStatus asset_catalog_undo_push_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:631
static const EnumPropertyItem * rna_asset_library_reference_itemf(bContext *, PointerRNA *, PropertyRNA *, bool *r_free)
Definition asset_ops.cc:818
static bool asset_catalog_operator_poll(bContext *C)
Definition asset_ops.cc:463
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
bool RNA_struct_is_ID(const StructRNA *type)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
char * RNA_string_get_alloc(PointerRNA *ptr, const char *name, char *fixedbuf, int fixedlen, int *r_len)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int_array(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
PropertyRNA * RNA_def_property(StructOrFunctionRNA *cont_, const char *identifier, int type, int subtype)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
const char * RE_engine_id_BLENDER_EEVEE_NEXT
Definition scene.cc:1626
const char * RE_engine_id_BLENDER_WORKBENCH
Definition scene.cc:1627
const char * RE_engine_id_BLENDER_EEVEE
Definition scene.cc:1625
Definition DNA_ID.h:404
struct AssetMetaData * asset_data
Definition DNA_ID.h:413
ImBufByteBuffer byte_buffer
void * first
char filepath[1024]
Definition BKE_main.hh:155
unsigned int h[2]
Definition DNA_ID.h:566
short flag[2]
Definition DNA_ID.h:567
unsigned int * rect[2]
Definition DNA_ID.h:569
unsigned int w[2]
Definition DNA_ID.h:565
char engine[32]
struct RenderData r
ListBase spacedata
View3DShading shading
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
struct ReportList * reports
struct PointerRNA * ptr
uint len
#define N_(msgid)
static DynamicLibrary lib
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
void WM_cursor_wait(bool val)
@ WM_CURSOR_CROSS
Definition wm_cursors.hh:26
uint8_t * WM_window_pixels_read(bContext *C, wmWindow *win, int r_size[2])
Definition wm_draw.cc:1451
void * WM_draw_cb_activate(wmWindow *win, void(*draw)(const wmWindow *win, void *customdata), void *customdata)
Definition wm_draw.cc:615
void WM_draw_cb_exit(wmWindow *win, void *handle)
Definition wm_draw.cc:628
void WM_event_add_fileselect(bContext *C, wmOperator *op)
void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ EVT_PADENTER
@ EVT_SPACEKEY
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_RIGHTSHIFTKEY
@ EVT_LEFTSHIFTKEY
@ EVT_RETKEY
PointerRNA * ptr
Definition wm_files.cc:4226
wmOperatorType * ot
Definition wm_files.cc:4225
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
bool WM_operator_winactive(bContext *C)
blender::int2 WM_window_native_pixel_size(const wmWindow *win)
uint8_t flag
Definition wm_window.cc:139