Blender V5.0
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
54#include "UI_interface_c.hh"
55#include "UI_resources.hh"
56
57namespace blender::ed::asset {
58/* -------------------------------------------------------------------- */
59
64struct IDVecStats {
65 bool has_asset = false;
66 bool has_supported_type = false;
67 bool is_single = false;
68};
69
75{
76 IDVecStats stats;
77
78 stats.is_single = id_pointers.size() == 1;
79
80 for (const PointerRNA &ptr : id_pointers) {
82
83 ID *id = static_cast<ID *>(ptr.data);
84 if (id_type_is_supported(id)) {
85 stats.has_supported_type = true;
86 }
87 if (ID_IS_ASSET(id)) {
88 stats.has_asset = true;
89 }
90 }
91
92 return stats;
93}
94
95static const char *asset_operation_unsupported_type_msg(const bool is_single)
96{
97 const char *msg_single =
98 "Data-block does not support asset operations - must be "
100 const char *msg_multiple =
101 "No data-block selected that supports asset operations - select at least "
103 return is_single ? msg_single : msg_multiple;
104}
105
106/* -------------------------------------------------------------------- */
107
109 public:
110 void operator()(const bContext &C, Span<PointerRNA> ids);
111
112 void reportResults(ReportList &reports) const;
113 bool wasSuccessful() const;
114
115 private:
116 struct Stats {
117 int tot_created = 0;
118 int tot_already_asset = 0;
119 ID *last_id = nullptr;
120 };
121
122 Stats stats;
123};
124
126{
127 for (const PointerRNA &ptr : ids) {
129
130 ID *id = static_cast<ID *>(ptr.data);
131 if (id->asset_data) {
132 stats.tot_already_asset++;
133 continue;
134 }
135
136 if (mark_id(id)) {
137 generate_preview(&C, id);
138
139 stats.last_id = id;
140 stats.tot_created++;
141 }
142 }
143}
144
146{
147 return stats.tot_created > 0;
148}
149
151{
152 /* User feedback on failure. */
153 if (!wasSuccessful()) {
154 if (stats.tot_already_asset > 0) {
155 BKE_report(&reports,
156 RPT_ERROR,
157 "Selected data-blocks are already assets (or do not support use as assets)");
158 }
159 else {
160 BKE_report(&reports,
161 RPT_ERROR,
162 "No data-blocks to create assets for found (or do not support use as assets)");
163 }
164 }
165 /* User feedback on success. */
166 else if (stats.tot_created == 1) {
167 /* If only one data-block: Give more useful message by printing asset name. */
168 BKE_reportf(&reports, RPT_INFO, "Data-block '%s' is now an asset", stats.last_id->name + 2);
169 }
170 else {
171 BKE_reportf(&reports, RPT_INFO, "%i data-blocks are now assets", stats.tot_created);
172 }
173}
174
176 const wmOperator *op,
177 const Span<PointerRNA> ids)
178{
179 AssetMarkHelper mark_helper;
180 mark_helper(*C, ids);
181 mark_helper.reportResults(*op->reports);
182
183 if (!mark_helper.wasSuccessful()) {
184 return OPERATOR_CANCELLED;
185 }
186
189
190 return OPERATOR_FINISHED;
191}
192
194{
196
197 if (!ctx_stats.has_supported_type) {
199 return false;
200 }
201
202 return true;
203}
204
206{
207 ot->name = "Mark as Asset";
208 ot->description =
209 "Enable easier reuse of selected data-blocks through the Asset Browser, with the help of "
210 "customizable metadata (like previews, descriptions and tags)";
211 ot->idname = "ASSET_OT_mark";
212
213 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
215 };
216 ot->poll = [](bContext *C) -> bool {
218 };
219
221}
222
227{
228 ot->name = "Mark as Single Asset";
229 ot->description =
230 "Enable easier reuse of a data-block through the Asset Browser, with the help of "
231 "customizable metadata (like previews, descriptions and tags)";
232 ot->idname = "ASSET_OT_mark_single";
233
234 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
236 };
237 ot->poll = [](bContext *C) -> bool {
239 };
240
242}
243
244/* -------------------------------------------------------------------- */
245
247 const bool set_fake_user_;
248
249 public:
250 AssetClearHelper(const bool set_fake_user) : set_fake_user_(set_fake_user) {}
251
253
254 void reportResults(const bContext *C, ReportList &reports) const;
255 bool wasSuccessful() const;
256
257 private:
258 struct Stats {
259 int tot_cleared = 0;
260 ID *last_id = nullptr;
261 };
262
263 Stats stats;
264};
265
267{
268 for (const PointerRNA &ptr : ids) {
270
271 ID *id = static_cast<ID *>(ptr.data);
272 if (!id->asset_data) {
273 continue;
274 }
275
276 if (!clear_id(id)) {
277 continue;
278 }
279
280 if (set_fake_user_) {
282 }
283
284 stats.tot_cleared++;
285 stats.last_id = id;
286 }
287}
288
290{
291 if (!wasSuccessful()) {
292 /* Dedicated error message for when there is an active asset detected, but it's not an ID local
293 * to this file. Helps users better understanding what's going on. */
294 if (AssetRepresentationHandle *active_asset = CTX_wm_asset(C); !active_asset->is_local_id()) {
295 BKE_report(&reports,
296 RPT_ERROR,
297 "No asset data-blocks from the current file selected (assets must be stored in "
298 "the current file to be able to edit or clear them)");
299 }
300 else {
301 BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused");
302 }
303 }
304 else if (stats.tot_cleared == 1) {
305 /* If only one data-block: Give more useful message by printing asset name. */
307 &reports, RPT_INFO, "Data-block '%s' is not an asset anymore", stats.last_id->name + 2);
308 }
309 else {
310 BKE_reportf(&reports, RPT_INFO, "%i data-blocks are not assets anymore", stats.tot_cleared);
311 }
312}
313
315{
316 return stats.tot_cleared > 0;
317}
318
320 const wmOperator *op,
321 const Span<PointerRNA> ids)
322{
323 const bool set_fake_user = RNA_boolean_get(op->ptr, "set_fake_user");
324 AssetClearHelper clear_helper(set_fake_user);
325 clear_helper(ids);
326 clear_helper.reportResults(C, *op->reports);
327
328 if (!clear_helper.wasSuccessful()) {
329 return OPERATOR_CANCELLED;
330 }
331
334
335 return OPERATOR_FINISHED;
336}
337
339{
341
342 if (!ctx_stats.has_asset) {
343 const char *msg_single = N_("Data-block is not marked as asset");
344 const char *msg_multiple = N_("No data-block selected that is marked as asset");
345 CTX_wm_operator_poll_msg_set(C, ctx_stats.is_single ? msg_single : msg_multiple);
346 return false;
347 }
348 if (!ctx_stats.has_supported_type) {
350 return false;
351 }
352
353 return true;
354}
355
356static std::string asset_clear_get_description(bContext * /*C*/,
357 wmOperatorType * /*ot*/,
359{
360 const bool set_fake_user = RNA_boolean_get(ptr, "set_fake_user");
361 if (!set_fake_user) {
362 return "";
363 }
364 return TIP_(
365 "Delete all asset metadata, turning the selected asset data-blocks back into normal "
366 "data-blocks, and set Fake User to ensure the data-blocks will still be saved");
367}
368
373{
374 ot->name = "Clear Asset";
375 ot->description =
376 "Delete all asset metadata and turn the selected asset data-blocks back into normal "
377 "data-blocks";
378 ot->get_description = asset_clear_get_description;
379 ot->idname = "ASSET_OT_clear";
380
381 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
383 };
384 ot->poll = [](bContext *C) -> bool {
386 };
387
389
390 RNA_def_boolean(ot->srna,
391 "set_fake_user",
392 false,
393 "Set Fake User",
394 "Ensure the data-block is saved, even when it is no longer marked as asset");
395}
396
398{
399 ot->name = "Clear Single Asset";
400 ot->description =
401 "Delete all asset metadata and turn the asset data-block back into a normal data-block";
402 ot->get_description = asset_clear_get_description;
403 ot->idname = "ASSET_OT_clear_single";
404
405 ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus {
407 };
408 ot->poll = [](bContext *C) -> bool {
410 };
411
413
414 RNA_def_boolean(ot->srna,
415 "set_fake_user",
416 false,
417 "Set Fake User",
418 "Ensure the data-block is saved, even when it is no longer marked as asset");
419}
420
421/* -------------------------------------------------------------------- */
422
424{
426 return true;
427 }
428
429 /* While not inside an Asset Browser, check if there's a asset list stored for the active asset
430 * library (stored in the workspace, obtained via context). */
432 if (!library) {
433 return false;
434 }
435
436 return list::has_list_storage_for_library(library) ||
438}
439
441{
443 /* Handles both global asset list storage and asset browsers. */
444 list::clear(library, C);
446
447 return OPERATOR_FINISHED;
448}
449
451{
452 /* identifiers */
453 ot->name = "Refresh Asset Library";
454 ot->description = "Reread assets and asset catalogs from the asset library on disk";
455 ot->idname = "ASSET_OT_library_refresh";
456
457 /* API callbacks. */
460}
461
462/* -------------------------------------------------------------------- */
463
465{
466 const SpaceFile *sfile = CTX_wm_space_file(C);
467 if (!sfile) {
468 return false;
469 }
471 if (!asset_library) {
472 return false;
473 }
474 if (catalogs_read_only(*asset_library)) {
475 CTX_wm_operator_poll_msg_set(C, "Asset catalogs cannot be edited in this asset library");
476 return false;
477 }
478 return true;
479}
480
482{
483 SpaceFile *sfile = CTX_wm_space_file(C);
485 std::string parent_path = RNA_string_get(op->ptr, "parent_path");
486
488 asset_library, DATA_("Catalog"), parent_path);
489
490 if (sfile) {
492 }
493
496
497 return OPERATOR_FINISHED;
498}
499
501{
502 /* identifiers */
503 ot->name = "New Asset Catalog";
504 ot->description = "Create a new catalog to put assets in";
505 ot->idname = "ASSET_OT_catalog_new";
506
507 /* API callbacks. */
510
511 RNA_def_string(ot->srna,
512 "parent_path",
513 nullptr,
514 0,
515 "Parent Path",
516 "Optional path defining the location to put the new catalog under");
517}
518
520{
521 SpaceFile *sfile = CTX_wm_space_file(C);
523 std::string catalog_id_str = RNA_string_get(op->ptr, "catalog_id");
524 asset_system::CatalogID catalog_id;
525 if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str.c_str())) {
526 return OPERATOR_CANCELLED;
527 }
528
529 catalog_remove(asset_library, catalog_id);
530
533
534 return OPERATOR_FINISHED;
535}
536
538{
539 /* identifiers */
540 ot->name = "Delete Asset Catalog";
541 ot->description =
542 "Remove an asset catalog from the asset library (contained assets will not be affected and "
543 "show up as unassigned)";
544 ot->idname = "ASSET_OT_catalog_delete";
545
546 /* API callbacks. */
549
550 RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete");
551}
552
554{
555 const SpaceFile *sfile = CTX_wm_space_file(C);
556 if (!sfile || ED_fileselect_is_file_browser(sfile)) {
557 return nullptr;
558 }
559
561 if (asset_lib) {
562 return &asset_lib->catalog_service();
563 }
564
565 return nullptr;
566}
567
569{
571 if (!catalog_service) {
572 return OPERATOR_CANCELLED;
573 }
574
575 catalog_service->undo();
577 return OPERATOR_FINISHED;
578}
579
581{
583 return catalog_service && catalog_service->is_undo_possbile();
584}
585
587{
588 /* identifiers */
589 ot->name = "Undo Catalog Edits";
590 ot->description = "Undo the last edit to the asset catalogs";
591 ot->idname = "ASSET_OT_catalog_undo";
592
593 /* API callbacks. */
596}
597
599{
601 if (!catalog_service) {
602 return OPERATOR_CANCELLED;
603 }
604
605 catalog_service->redo();
607 return OPERATOR_FINISHED;
608}
609
611{
613 return catalog_service && catalog_service->is_redo_possbile();
614}
615
617{
618 /* identifiers */
619 ot->name = "Redo Catalog Edits";
620 ot->description = "Redo the last undone edit to the asset catalogs";
621 ot->idname = "ASSET_OT_catalog_redo";
622
623 /* API callbacks. */
626}
627
629{
631 if (!catalog_service) {
632 return OPERATOR_CANCELLED;
633 }
634
635 catalog_service->undo_push();
636 return OPERATOR_FINISHED;
637}
638
640{
641 return get_catalog_service(C) != nullptr;
642}
643
645{
646 /* identifiers */
647 ot->name = "Store undo snapshot for asset catalog edits";
648 ot->description = "Store the current state of the asset catalogs in the undo buffer";
649 ot->idname = "ASSET_OT_catalog_undo_push";
650
651 /* API callbacks. */
654
655 /* Generally artists don't need to find & use this operator, it's meant for scripts only. */
656 ot->flag = OPTYPE_INTERNAL;
657}
658
659/* -------------------------------------------------------------------- */
660
662{
664 return false;
665 }
666
667 const Main *bmain = CTX_data_main(C);
668 if (!bmain->filepath[0]) {
669 CTX_wm_operator_poll_msg_set(C, "Cannot save asset catalogs before the Blender file is saved");
670 return false;
671 }
672
674 CTX_wm_operator_poll_msg_set(C, "No changes to be saved");
675 return false;
676 }
677
678 return true;
679}
680
693
695{
696 /* identifiers */
697 ot->name = "Save Asset Catalogs";
698 ot->description =
699 "Make any edits to any catalogs permanent by writing the current set up to the asset "
700 "library";
701 ot->idname = "ASSET_OT_catalogs_save";
702
703 /* API callbacks. */
706}
707
708/* -------------------------------------------------------------------- */
709
710static bool could_be_asset_bundle(const Main *bmain);
712static bool is_contained_in_selected_asset_library(wmOperator *op, const char *filepath);
713static bool set_filepath_for_asset_lib(const Main *bmain, wmOperator *op);
714static bool has_external_files(Main *bmain, ReportList *reports);
715
717{
718 /* This operator only works when the asset browser is set to Current File. */
719 const SpaceFile *sfile = CTX_wm_space_file(C);
720 if (sfile == nullptr) {
721 return false;
722 }
724 return false;
725 }
726
727 const Main *bmain = CTX_data_main(C);
728 if (!could_be_asset_bundle(bmain)) {
729 return false;
730 }
731
732 /* Check whether this file is already located inside any asset library. */
734 &U, bmain->filepath);
735 if (asset_lib) {
736 return false;
737 }
738
739 return true;
740}
741
743 wmOperator *op,
744 const wmEvent * /*event*/)
745{
746 Main *bmain = CTX_data_main(C);
747 if (has_external_files(bmain, op->reports)) {
748 return OPERATOR_CANCELLED;
749 }
750
752
753 /* Make the "Save As" dialog box default to "${ASSET_LIB_ROOT}/${CURRENT_FILE}.blend". */
754 if (!set_filepath_for_asset_lib(bmain, op)) {
755 return OPERATOR_CANCELLED;
756 }
757
759}
760
762{
763 Main *bmain = CTX_data_main(C);
764 if (has_external_files(bmain, op->reports)) {
765 return OPERATOR_CANCELLED;
766 }
767
768 /* Check file path, copied from #wm_file_write(). */
769 char filepath[FILE_MAX];
770 RNA_string_get(op->ptr, "filepath", filepath);
771 const size_t len = strlen(filepath);
772
773 if (len == 0) {
774 BKE_report(op->reports, RPT_ERROR, "Path is empty, cannot save");
775 return OPERATOR_CANCELLED;
776 }
777
778 if (len >= FILE_MAX) {
779 BKE_report(op->reports, RPT_ERROR, "Path too long, cannot save");
780 return OPERATOR_CANCELLED;
781 }
782
783 /* Check that the destination is actually contained in the selected asset library. */
784 if (!is_contained_in_selected_asset_library(op, filepath)) {
785 BKE_reportf(op->reports, RPT_ERROR, "Selected path is outside of the selected asset library");
786 return OPERATOR_CANCELLED;
787 }
788
789 WM_cursor_wait(true);
791 /* Store undo step, such that on a failed save the 'prepare_to_merge_on_write' call can be
792 * un-done. */
793 cat_service->undo_push();
794 cat_service->prepare_to_merge_on_write();
795
796 const wmOperatorStatus operator_result = WM_operator_name_call(
797 C, "WM_OT_save_mainfile", wm::OpCallContext::ExecDefault, op->ptr, nullptr);
798 WM_cursor_wait(false);
799
800 if (operator_result != OPERATOR_FINISHED) {
801 cat_service->undo();
802 return operator_result;
803 }
804
806 BLI_assert_msg(lib, "If the asset library is not known, how did we get here?");
808 RPT_INFO,
809 R"(Saved "%s" to asset library "%s")",
811 lib->name);
812 return OPERATOR_FINISHED;
813}
814
816 PointerRNA * /*ptr*/,
817 PropertyRNA * /*prop*/,
818 bool *r_free)
819{
821 if (!items) {
822 *r_free = false;
823 return nullptr;
824 }
825
826 *r_free = true;
827 return items;
828}
829
831{
832 /* identifiers */
833 ot->name = "Copy to Asset Library";
834 ot->description =
835 "Copy the current .blend file into an Asset Library. Only works on standalone .blend files "
836 "(i.e. when no other files are referenced)";
837 ot->idname = "ASSET_OT_bundle_install";
838
839 /* API callbacks. */
843
844 ot->prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE);
847
851 FILE_SAVE,
855}
856
857/* Cheap check to see if this is an "asset bundle" just by checking main file name.
858 * A proper check will be done in the exec function, to ensure that no external files will be
859 * referenced. */
860static bool could_be_asset_bundle(const Main *bmain)
861{
862 return fnmatch("*_bundle.blend", bmain->filepath, FNM_CASEFOLD) == 0;
863}
864
866{
867 const int enum_value = RNA_enum_get(op->ptr, "asset_library_reference");
870 &U, lib_ref.custom_library_index);
871 return lib;
872}
873
874static bool is_contained_in_selected_asset_library(wmOperator *op, const char *filepath)
875{
877 if (!lib) {
878 return false;
879 }
880 return BLI_path_contains(lib->dirpath, filepath);
881}
882
887static bool set_filepath_for_asset_lib(const Main *bmain, wmOperator *op)
888{
889 /* Find the directory path of the selected asset library. */
891 if (lib == nullptr) {
892 return false;
893 }
894
895 /* Concatenate the filename of the current blend file. */
896 const char *blend_filename = BLI_path_basename(bmain->filepath);
897 if (blend_filename == nullptr || blend_filename[0] == '\0') {
898 return false;
899 }
900
901 char file_path[FILE_MAX];
902 BLI_path_join(file_path, sizeof(file_path), lib->dirpath, blend_filename);
903 RNA_string_set(op->ptr, "filepath", file_path);
904
905 return true;
906}
907
912
914 char * /*path_dst*/,
915 size_t /*path_dst_maxncpy*/,
916 const char *path_src)
917{
918 FileCheckCallbackInfo *callback_info = static_cast<FileCheckCallbackInfo *>(
919 bpath_data->user_data);
920 callback_info->external_files.add(std::string(path_src));
921 return false;
922}
923
931static bool has_external_files(Main *bmain, ReportList *reports)
932{
933 FileCheckCallbackInfo callback_info = {reports, Set<std::string>()};
934
936 (BKE_BPATH_FOREACH_PATH_SKIP_PACKED /* Packed files are fine. */
937 | BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE /* Only report multi-files once, it's enough. */
938 | BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES); /* Only care about actually used files. */
939
940 BPathForeachPathData bpath_data = {
941 /*bmain*/ bmain,
942 /*callback_function*/ &external_file_check_callback,
943 /*flag*/ flag,
944 /*user_data*/ &callback_info,
945 /*absolute_base_path*/ nullptr,
946 };
947 BKE_bpath_foreach_path_main(&bpath_data);
948
949 if (callback_info.external_files.is_empty()) {
950 /* No external dependencies. */
951 return false;
952 }
953
954 if (callback_info.external_files.size() == 1) {
955 /* Only one external dependency, report it directly. */
956 BKE_reportf(callback_info.reports,
957 RPT_ERROR,
958 "Unable to copy bundle due to external dependency: \"%s\"",
959 callback_info.external_files.begin()->c_str());
960 return true;
961 }
962
963 /* Multiple external dependencies, report the aggregate and put details on console. */
965 callback_info.reports,
966 RPT_ERROR,
967 "Unable to copy bundle due to %zu external dependencies; more details on the console",
968 size_t(callback_info.external_files.size()));
969 printf("Unable to copy bundle due to %zu external dependencies:\n",
970 size_t(callback_info.external_files.size()));
971 for (const std::string &path : callback_info.external_files) {
972 printf(" \"%s\"\n", path.c_str());
973 }
974 return true;
975}
976
977constexpr int DRAG_THRESHOLD = 4;
978
982 /* Screenshot points may not be set immediately to allow for clicking to create a screenshot with
983 * the previous size. */
985
987 /* Dragged far enough to create the screenshot are instead of registering as a click. */
989 /* Move the whole screenshot area when moving the cursor instead of placing `drag_end`. */
992};
993
994/* Sort points so p1 is lower left, and p2 is top right. */
995static inline void sort_points(int2 &p1, int2 &p2)
996{
997 if (p1.x > p2.x) {
998 std::swap(p1.x, p2.x);
999 }
1000 if (p1.y > p2.y) {
1001 std::swap(p1.y, p2.y);
1002 }
1003}
1004
1005/* Clamps the point to the window bounds. */
1006static inline int2 clamp_point_to_window(const int2 &point, const wmWindow *window)
1007{
1008 const int2 win_size = WM_window_native_pixel_size(window);
1009 return {clamp_i(point.x, 0, win_size.x - 1), clamp_i(point.y, 0, win_size.y - 1)};
1010}
1011
1012/* Ensures that the x and y distance to from p1 to p2 is equal and the resulting square remains
1013 * fully within the window bounds. The two points can be in any spacial relation to each other i.e.
1014 * if p1 was top left, it remains top left. */
1015static inline void square_points_clamp_to_window(const int2 &p1, int2 &p2, const wmWindow *window)
1016{
1017 const int2 delta = p2 - p1;
1018
1019 /* Determine the drag direction for each axis. */
1020 const int dir_x = (delta.x >= 0) ? 1 : -1;
1021 const int dir_y = (delta.y >= 0) ? 1 : -1;
1022
1023 const int size_x = std::abs(delta.x);
1024 const int size_y = std::abs(delta.y);
1025 int square_size = std::max(size_x, size_y);
1026
1027 /* Compute maximum size that fits within window bounds in the drag direction. */
1028 const int2 win_size = WM_window_native_pixel_size(window);
1029 const int max_size_x = (dir_x > 0) ? win_size.x - p1.x - 1 : p1.x;
1030 const int max_size_y = (dir_y > 0) ? win_size.y - p1.y - 1 : p1.y;
1031
1032 /* Clamp the square size so it does not exceed window bounds. */
1033 square_size = std::min(square_size, std::min(max_size_x, max_size_y));
1034
1035 /* Update p2 to form a clamped square in the same direction as the drag. */
1036 p2.x = p1.x + dir_x * square_size;
1037 p2.y = p1.y + dir_y * square_size;
1038}
1039
1040static void generate_previewimg_from_buffer(ID *id, const ImBuf *image_buffer)
1041{
1042 PreviewImage *preview_image = BKE_previewimg_id_ensure(id);
1043 BKE_previewimg_clear(preview_image);
1044
1045 for (int size_type = 0; size_type < NUM_ICON_SIZES; size_type++) {
1046 BKE_previewimg_ensure(preview_image, size_type);
1047 int width = image_buffer->x;
1048 int height = image_buffer->y;
1049 int max_size = 0;
1050 switch (size_type) {
1051 case ICON_SIZE_ICON:
1052 max_size = ICON_RENDER_DEFAULT_HEIGHT;
1053 break;
1054 case ICON_SIZE_PREVIEW:
1055 max_size = PREVIEW_RENDER_LARGE_HEIGHT;
1056 break;
1057 }
1058 if (max_size == 0) {
1059 /* Can only be reached if a new icon size is added. */
1061 continue;
1062 }
1063
1064 /* Scales down the image to `max_size` while maintaining the
1065 * aspect ratio. */
1066 if (image_buffer->x > image_buffer->y) {
1067 width = max_size;
1068 height = image_buffer->y * (width / float(image_buffer->x));
1069 }
1070 else if (image_buffer->y > image_buffer->x) {
1071 height = max_size;
1072 width = image_buffer->x * (height / float(image_buffer->y));
1073 }
1074 else {
1075 width = height = max_size;
1076 }
1077
1078 ImBuf *scaled_imbuf = IMB_scale_into_new(
1079 image_buffer, width, height, IMBScaleFilter::Nearest, false);
1080 preview_image->rect[size_type] = (uint *)MEM_dupallocN(scaled_imbuf->byte_buffer.data);
1081 preview_image->w[size_type] = width;
1082 preview_image->h[size_type] = height;
1083 preview_image->flag[size_type] |= PRV_USER_EDITED;
1084 IMB_freeImBuf(scaled_imbuf);
1085 }
1086}
1087
1092static ImBuf *take_screenshot_crop(bContext *C, const rcti &crop_rect)
1093{
1094 int dumprect_size[2];
1095 wmWindow *win = CTX_wm_window(C);
1096 uint8_t *dumprect = WM_window_pixels_read(C, win, dumprect_size);
1097
1098 /* Clamp coordinates to window bounds. */
1099 rcti safe_rect = crop_rect;
1100 safe_rect.xmin = max_ii(0, crop_rect.xmin);
1101 safe_rect.ymin = max_ii(0, crop_rect.ymin);
1102 safe_rect.xmax = min_ii(dumprect_size[0] - 1, crop_rect.xmax);
1103 safe_rect.ymax = min_ii(dumprect_size[1] - 1, crop_rect.ymax);
1104
1105 /* Validate rectangle. */
1106 if (!BLI_rcti_is_valid(&safe_rect)) {
1107 MEM_freeN(dumprect);
1108 return nullptr;
1109 }
1110
1111 ImBuf *image_buffer = IMB_allocImBuf(dumprect_size[0], dumprect_size[1], 24, 0);
1112 /* Using IB_TAKE_OWNERSHIP because the crop does kind of take ownership already it seems. At
1113 * least freeing the memory after would cause a crash if ownership isn't taken. */
1114 IMB_assign_byte_buffer(image_buffer, dumprect, IB_TAKE_OWNERSHIP);
1115
1116 IMB_rect_crop(image_buffer, &safe_rect);
1117 return image_buffer;
1118}
1119
1121{
1122 int2 p1, p2;
1123 wmWindow *win = CTX_wm_window(C);
1124 RNA_int_get_array(op->ptr, "p1", p1);
1125 RNA_int_get_array(op->ptr, "p2", p2);
1126
1127 /* Clamp points to window bounds, so the screenshot area is always valid. */
1128 p1 = clamp_point_to_window(p1, win);
1129 p2 = clamp_point_to_window(p2, win);
1130
1131 /* Squaring has to happen before sorting so the area is squared from the point where
1132 * dragging started. */
1133 if (RNA_boolean_get(op->ptr, "force_square")) {
1134 square_points_clamp_to_window(p1, p2, win);
1135 }
1136
1137 sort_points(p1, p2);
1138
1139 /* The min side is chosen arbitrarily to avoid accidental creations of very small screenshots. */
1140 constexpr int min_side = 16;
1141 if (p2.x - p1.x < min_side || p2.y - p1.y < min_side) {
1143 op->reports, RPT_ERROR, "Screenshot cannot be smaller than %i pixels on a side", min_side);
1144 return OPERATOR_CANCELLED;
1145 }
1146
1147 ImBuf *image_buffer;
1148
1151 /* Special case for taking a screenshot from a 3D viewport. In that case we do an offscreen
1152 * render to support transparency. Render settings are used as currently set up in the viewport
1153 * to comply with WYSIWYG as much as possible. One limitation is that GUI elements will not be
1154 * visible in the render. */
1155 bool render_offscreen = false;
1156 if (area_p1 == area_p2 && area_p1 != nullptr && area_p1->spacetype == SPACE_VIEW3D) {
1157 Scene *scene = CTX_data_scene(C);
1158 View3D *v3d = static_cast<View3D *>(area_p1->spacedata.first);
1159 /* For #ED_view3d_draw_offscreen_imbuf only EEVEE only produces a good result. See #141732. */
1160 if (eDrawType(v3d->shading.type) == OB_RENDER) {
1161 const char *engine_name = scene->r.engine;
1162 render_offscreen = STR_ELEM(engine_name,
1166 }
1167 else {
1168 render_offscreen = true;
1169 }
1170 }
1171 if (render_offscreen) {
1172 View3D *v3d = static_cast<View3D *>(area_p1->spacedata.first);
1174 if (!region) {
1175 /* Unlikely to be hit, but just being cautious. */
1177 return OPERATOR_CANCELLED;
1178 }
1179 char err_out[256] = "unknown";
1182 eDrawType(v3d->shading.type),
1183 v3d,
1184 region,
1185 region->winx,
1186 region->winy,
1189 nullptr,
1190 false,
1191 nullptr,
1192 nullptr,
1193 err_out);
1194
1195 /* Convert crop rect into the space relative to the area. */
1196 const rcti crop_rect = {p1.x - area_p1->totrct.xmin,
1197 p2.x - area_p1->totrct.xmin,
1198 p1.y - area_p1->totrct.ymin,
1199 p2.y - area_p1->totrct.ymin};
1200 IMB_rect_crop(image_buffer, &crop_rect);
1201 }
1202 else {
1203 const rcti crop_rect = {p1.x, p2.x, p1.y, p2.y};
1204 image_buffer = take_screenshot_crop(C, crop_rect);
1205 if (!image_buffer) {
1206 BKE_report(op->reports, RPT_ERROR, "Invalid screenshot area selection");
1207 return OPERATOR_CANCELLED;
1208 }
1209 }
1210
1211 const AssetRepresentationHandle *asset_handle = CTX_wm_asset(C);
1212 BLI_assert_msg(asset_handle != nullptr, "This is ensured by poll");
1213 AssetWeakReference asset_reference = asset_handle->make_weak_reference();
1214
1215 Main *bmain = CTX_data_main(C);
1217 *bmain, asset_handle->get_id_type(), asset_reference);
1218 BLI_assert(id != nullptr);
1219
1221
1222 generate_previewimg_from_buffer(id, image_buffer);
1223 IMB_freeImBuf(image_buffer);
1224
1226 const bool saved = bke::asset_edit_id_save(*bmain, *id, *op->reports);
1227 if (!saved) {
1228 BKE_report(op->reports, RPT_ERROR, "Saving failed");
1229 }
1230 }
1231
1234
1236
1237 return OPERATOR_FINISHED;
1238}
1239
1240static void screenshot_preview_draw(const wmWindow *window, void *operator_data)
1241{
1242 ScreenshotOperatorData *data = static_cast<ScreenshotOperatorData *>(operator_data);
1243 int2 p1 = data->p1;
1244 int2 p2 = data->p2;
1245
1246 /* Clamp points to window bounds, so the screenshot area is always valid. */
1247 p1 = clamp_point_to_window(p1, window);
1248 p2 = clamp_point_to_window(p2, window);
1249
1250 /* Squaring has to happen before sorting so the area is squared from the point where
1251 * dragging started. */
1252 if (data->force_square) {
1253 square_points_clamp_to_window(p1, p2, window);
1254 }
1255
1256 sort_points(p1, p2);
1257
1258 /* Drawing rect just out of the screenshot area to not capture the box in the picture. */
1259 const rctf screenshot_rect = {
1260 float(p1.x - 1), float(p2.x + 1), float(p1.y - 1), float(p2.y + 1)};
1261
1262 /* Drawing a semi-transparent mask to highlight the area that will be captured. */
1263 float4 mask_color = {1, 1, 1, 0.25};
1264 const int2 win_size = WM_window_native_pixel_size(window);
1265 const rctf mask_rect_bottom = {0, float(win_size.x), 0, screenshot_rect.ymin};
1266 UI_draw_roundbox_aa(&mask_rect_bottom, true, 0, mask_color);
1267 const rctf mask_rect_top = {0, float(win_size.x), screenshot_rect.ymax, float(win_size.y)};
1268 UI_draw_roundbox_aa(&mask_rect_top, true, 0, mask_color);
1269 const rctf mask_rect_left = {
1270 0, screenshot_rect.xmin, screenshot_rect.ymin, screenshot_rect.ymax};
1271 UI_draw_roundbox_aa(&mask_rect_left, true, 0, mask_color);
1272 const rctf mask_rect_right = {
1273 screenshot_rect.xmax, float(win_size.x), screenshot_rect.ymin, screenshot_rect.ymax};
1274 UI_draw_roundbox_aa(&mask_rect_right, true, 0, mask_color);
1275
1276 float4 color;
1278 UI_draw_roundbox_aa(&screenshot_rect, false, 0, color);
1279}
1280
1282{
1283 wmWindow *win = CTX_wm_window(C);
1286 WM_draw_cb_exit(win, data->draw_handle);
1287 MEM_freeN(data);
1288 ED_workspace_status_text(C, nullptr);
1289}
1290
1292{
1293 RNA_boolean_set(op->ptr, "force_square", data->force_square);
1294 RNA_int_set_array(op->ptr, "p1", data->p1);
1295 RNA_int_set_array(op->ptr, "p2", data->p2);
1296}
1297
1299{
1300 ARegion *region = CTX_wm_region(C);
1301 wmWindow *win = CTX_wm_window(C);
1303
1304 const int2 screen_space_cursor = {
1305 event->mval[0] + region->winrct.xmin,
1306 event->mval[1] + region->winrct.ymin,
1307 };
1308 switch (event->type) {
1309 case LEFTMOUSE: {
1310 switch (event->val) {
1311 case KM_PRESS:
1312 data->is_mouse_down = true;
1313 data->crossed_threshold = false;
1314 data->drag_start = screen_space_cursor;
1315 break;
1316 case KM_RELEASE:
1317 data->is_mouse_down = false;
1318 data->drag_end = clamp_point_to_window(screen_space_cursor, win);
1322 return OPERATOR_FINISHED;
1323 }
1324 break;
1325 }
1326
1327 case EVT_PADENTER:
1328 case EVT_RETKEY: {
1332 return OPERATOR_FINISHED;
1333 }
1334
1335 case RIGHTMOUSE:
1336 case EVT_ESCKEY: {
1338 CTX_wm_screen(C)->do_draw = true;
1339 return OPERATOR_CANCELLED;
1340 }
1341
1342 case EVT_SPACEKEY: {
1343 switch (event->val) {
1344 case KM_PRESS:
1345 data->shift_area = true;
1346 break;
1347 case KM_RELEASE:
1348 data->shift_area = false;
1349 break;
1350
1351 default:
1352 break;
1353 }
1354 break;
1355 }
1356
1357 case EVT_LEFTSHIFTKEY:
1358 case EVT_RIGHTSHIFTKEY: {
1359 switch (event->val) {
1360 case KM_PRESS:
1361 data->force_square = false;
1362 break;
1363 case KM_RELEASE:
1364 data->force_square = true;
1365 break;
1366
1367 default:
1368 break;
1369 }
1370 break;
1371 }
1372
1373 case MOUSEMOVE: {
1374 if (data->shift_area) {
1375 const int2 delta = screen_space_cursor - data->last_cursor;
1376 const int2 new_p1 = data->p1 + delta;
1377 const int2 new_p2 = data->p2 + delta;
1378
1379 auto is_within_window = [win](const int2 &pt) -> bool {
1380 const int2 win_size = WM_window_native_pixel_size(win);
1381 return pt.x >= 0 && pt.x < win_size.x && pt.y >= 0 && pt.y < win_size.y;
1382 };
1383
1384 /* Apply movement only if the entire rectangle stays within window bounds. */
1385 if (is_within_window(new_p1) && is_within_window(new_p2)) {
1386 data->p1 = new_p1;
1387 data->p2 = new_p2;
1388 }
1389 }
1390 else if (data->is_mouse_down) {
1391 data->drag_end = clamp_point_to_window(screen_space_cursor, win);
1392
1393 if (!data->crossed_threshold) {
1394 const int2 delta = data->drag_end - data->drag_start;
1395 if (std::abs(delta.x) > DRAG_THRESHOLD && std::abs(delta.y) > DRAG_THRESHOLD) {
1396 /* Only set the points once the threshold has been crossed. This allows to just
1397 * click to confirm using a potentially existing screenshot rect. */
1398 data->crossed_threshold = true;
1399 data->p1 = data->drag_start;
1400 }
1401 }
1402
1403 if (data->crossed_threshold) {
1404 data->p2 = data->drag_end;
1405 }
1406 }
1407
1408 CTX_wm_screen(C)->do_draw = true;
1409 data->last_cursor = screen_space_cursor;
1410 break;
1411 }
1412
1413 default:
1414 break;
1415 }
1416
1418 if (data->is_mouse_down) {
1419 status.item(IFACE_("Cancel"), ICON_EVENT_ESC, ICON_MOUSE_RMB);
1420 }
1421 else {
1422 status.item(IFACE_("Start"), ICON_MOUSE_LMB_DRAG);
1423 }
1424 status.item(IFACE_("Confirm"), ICON_MOUSE_LMB, ICON_EVENT_RETURN);
1425 status.item(IFACE_("Move"), ICON_EVENT_SPACEKEY);
1426 status.item(IFACE_("Unlock Aspect Ratio"), ICON_EVENT_SHIFT);
1427
1429}
1430
1432 wmOperator *op,
1433 const wmEvent * /* event */)
1434{
1435 wmWindow *win = CTX_wm_window(C);
1437
1438 op->customdata = MEM_callocN(sizeof(ScreenshotOperatorData), __func__);
1441 data->is_mouse_down = false;
1442 RNA_int_get_array(op->ptr, "p1", data->p1);
1443 RNA_int_get_array(op->ptr, "p2", data->p2);
1444 data->last_cursor = data->p1;
1445 data->shift_area = false;
1446 data->crossed_threshold = false;
1447 data->force_square = RNA_boolean_get(op->ptr, "force_square");
1448
1450 CTX_wm_screen(C)->do_draw = true;
1451
1453}
1454
1456{
1457 if (G.background) {
1458 return false;
1459 }
1460
1461 const AssetRepresentationHandle *asset_handle = CTX_wm_asset(C);
1462 if (!asset_handle) {
1463 CTX_wm_operator_poll_msg_set(C, "No selected asset");
1464 return false;
1465 }
1466 if (asset_handle->is_local_id()) {
1467 return WM_operator_winactive(C);
1468 }
1469
1470 std::string lib_path = asset_handle->full_library_path();
1471 if (StringRef(lib_path).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
1472 return true;
1473 }
1474
1475 CTX_wm_operator_poll_msg_set(C, "Asset cannot be modified from this file");
1476 return false;
1477}
1478
1480{
1481 /* This should be a generic operator for assets not linked to the pose-library. */
1482
1483 ot->name = "Capture Screenshot Preview";
1484 ot->description = "Capture a screenshot to use as a preview for the selected asset";
1485 ot->idname = "ASSET_OT_screenshot_preview";
1486
1488 ot->invoke = screenshot_preview_invoke;
1491
1492 RNA_def_int_array(ot->srna,
1493 "p1",
1494 2,
1495 nullptr,
1496 0,
1497 INT_MAX,
1498 "Point 1",
1499 "First point of the screenshot in screenspace",
1500 0,
1501 3840);
1502 RNA_def_int_array(ot->srna,
1503 "p2",
1504 2,
1505 nullptr,
1506 0,
1507 INT_MAX,
1508 "Point 2",
1509 "Second point of the screenshot in screenspace",
1510 0,
1511 3840);
1512 RNA_def_boolean(ot->srna,
1513 "force_square",
1514 true,
1515 "Force Square",
1516 "If enabled, the screenshot will have the same height as width");
1517}
1518
1519/* -------------------------------------------------------------------- */
1520
1540
1541} // 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:32
@ BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES
Definition BKE_bpath.hh:55
@ BKE_BPATH_FOREACH_PATH_SKIP_PACKED
Definition BKE_bpath.hh:42
@ BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE
Definition BKE_bpath.hh:69
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:396
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
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:846
#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:661
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)
#define ID_IS_ASSET(_id)
Definition DNA_ID.h:737
@ PRV_USER_EDITED
Definition DNA_ID.h:620
@ 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:500
bool ED_fileselect_is_file_browser(const SpaceFile *sfile)
Definition filesel.cc:462
bool ED_fileselect_is_local_asset_library(const SpaceFile *sfile)
Definition filesel.cc:407
blender::asset_system::AssetLibrary * ED_fileselect_active_asset_library_get(const SpaceFile *sfile)
Definition filesel.cc:472
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:3988
bool ED_operator_asset_browsing_active(bContext *C)
void ED_workspace_status_text(bContext *C, const char *str)
Definition area.cc:1024
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:241
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:489
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:166
@ PROP_HIDDEN
Definition RNA_types.hh:338
@ PROP_NONE
Definition RNA_types.hh:233
#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:1124
@ FILE_SAVE
Definition WM_api.hh:1134
#define ND_ASSET_LIST_READING
Definition WM_types.hh:550
#define NC_ID
Definition WM_types.hh:395
@ KM_PRESS
Definition WM_types.hh:311
@ KM_RELEASE
Definition WM_types.hh:312
#define ND_SPACE_ASSET_PARAMS
Definition WM_types.hh:525
#define NA_ADDED
Definition WM_types.hh:586
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_ASSET
Definition WM_types.hh:404
#define NA_REMOVED
Definition WM_types.hh:587
#define ND_ASSET_LIST
Definition WM_types.hh:548
#define ND_ASSET_CATALOGS
Definition WM_types.hh:554
#define NC_SPACE
Definition WM_types.hh:392
#define U
BMesh const char void * data
int size_type
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:289
void operator()(Span< PointerRNA > ids)
Definition asset_ops.cc:266
AssetClearHelper(const bool set_fake_user)
Definition asset_ops.cc:250
void operator()(const bContext &C, Span< PointerRNA > ids)
Definition asset_ops.cc:125
void reportResults(ReportList &reports) const
Definition asset_ops.cc:150
nullptr float
#define printf(...)
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:661
static bool could_be_asset_bundle(const Main *bmain)
Definition asset_ops.cc:860
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:639
static bool external_file_check_callback(BPathForeachPathData *bpath_data, char *, size_t, const char *path_src)
Definition asset_ops.cc:913
static void ASSET_OT_clear(wmOperatorType *ot)
Definition asset_ops.cc:372
static wmOperatorStatus asset_catalogs_save_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:681
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:616
AssetLibraryReference library_reference_from_enum_value(int value)
static std::string asset_clear_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
Definition asset_ops.cc:356
static void ASSET_OT_catalog_new(wmOperatorType *ot)
Definition asset_ops.cc:500
static bool asset_catalog_redo_poll(bContext *C)
Definition asset_ops.cc:610
static void ASSET_OT_clear_single(wmOperatorType *ot)
Definition asset_ops.cc:397
static void ASSET_OT_catalog_delete(wmOperatorType *ot)
Definition asset_ops.cc:537
static wmOperatorStatus asset_bundle_install_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition asset_ops.cc:742
static IDVecStats asset_operation_get_id_vec_stats_from_ids(const Span< PointerRNA > id_pointers)
Definition asset_ops.cc:74
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:226
static bool is_contained_in_selected_asset_library(wmOperator *op, const char *filepath)
Definition asset_ops.cc:874
static bool asset_clear_poll(bContext *C, const Span< PointerRNA > ids)
Definition asset_ops.cc:338
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:481
static bool asset_bundle_install_poll(bContext *C)
Definition asset_ops.cc:716
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:644
static bool set_filepath_for_asset_lib(const Main *bmain, wmOperator *op)
Definition asset_ops.cc:887
static wmOperatorStatus screenshot_preview_exec(bContext *C, wmOperator *op)
static wmOperatorStatus asset_catalog_redo_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:598
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:977
static void ASSET_OT_bundle_install(wmOperatorType *ot)
Definition asset_ops.cc:830
static bool screenshot_preview_poll(bContext *C)
static const char * asset_operation_unsupported_type_msg(const bool is_single)
Definition asset_ops.cc:95
static bool asset_catalog_undo_poll(bContext *C)
Definition asset_ops.cc:580
static wmOperatorStatus asset_library_refresh_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:440
static void screenshot_preview_exit(bContext *C, wmOperator *op)
static bool asset_mark_poll(bContext *C, const Span< PointerRNA > ids)
Definition asset_ops.cc:193
static void ASSET_OT_mark(wmOperatorType *ot)
Definition asset_ops.cc:205
static wmOperatorStatus asset_catalog_delete_exec(bContext *C, wmOperator *op)
Definition asset_ops.cc:519
static void ASSET_OT_catalog_undo(wmOperatorType *ot)
Definition asset_ops.cc:586
static bool asset_library_refresh_poll(bContext *C)
Definition asset_ops.cc:423
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:553
static void ASSET_OT_screenshot_preview(wmOperatorType *ot)
static void sort_points(int2 &p1, int2 &p2)
Definition asset_ops.cc:995
static wmOperatorStatus asset_catalog_undo_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:568
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:865
static wmOperatorStatus asset_clear_exec(const bContext *C, const wmOperator *op, const Span< PointerRNA > ids)
Definition asset_ops.cc:319
static wmOperatorStatus asset_bundle_install_exec(bContext *C, wmOperator *op)
Definition asset_ops.cc:761
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:175
static void ASSET_OT_library_refresh(wmOperatorType *ot)
Definition asset_ops.cc:450
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:694
static bool has_external_files(Main *bmain, ReportList *reports)
Definition asset_ops.cc:931
static wmOperatorStatus asset_catalog_undo_push_exec(bContext *C, wmOperator *)
Definition asset_ops.cc:628
static const EnumPropertyItem * rna_asset_library_reference_itemf(bContext *, PointerRNA *, PropertyRNA *, bool *r_free)
Definition asset_ops.cc:815
static bool asset_catalog_operator_poll(bContext *C)
Definition asset_ops.cc:464
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
const int status
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)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
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:1581
const char * RE_engine_id_BLENDER_WORKBENCH
Definition scene.cc:1582
const char * RE_engine_id_BLENDER_EEVEE
Definition scene.cc:1580
Definition DNA_ID.h:414
struct AssetMetaData * asset_data
Definition DNA_ID.h:423
ImBufByteBuffer byte_buffer
void * first
char filepath[1024]
Definition BKE_main.hh:179
unsigned int h[2]
Definition DNA_ID.h:643
short flag[2]
Definition DNA_ID.h:644
unsigned int * rect[2]
Definition DNA_ID.h:646
unsigned int w[2]
Definition DNA_ID.h:642
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:757
short val
Definition WM_types.hh:759
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:1484
void * WM_draw_cb_activate(wmWindow *win, void(*draw)(const wmWindow *win, void *customdata), void *customdata)
Definition wm_draw.cc:635
void WM_draw_cb_exit(wmWindow *win, void *handle)
Definition wm_draw.cc:648
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)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
@ RIGHTMOUSE
@ EVT_PADENTER
@ EVT_SPACEKEY
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
@ EVT_RIGHTSHIFTKEY
@ EVT_LEFTSHIFTKEY
@ EVT_RETKEY
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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:145