A renaming of UI scale factors from names that imply a relationship to monitor DPI to names that imply that they simply change "scale" Pull Request: blender/blender#105750
879 lines
27 KiB
C
879 lines
27 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2008 Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup edutil
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_path_util.h"
|
|
#include "BLI_rect.h"
|
|
#include "BLI_string.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_image.h"
|
|
|
|
#include "BLF_api.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_metadata.h"
|
|
|
|
#include "ED_screen.h"
|
|
#include "ED_space_api.h"
|
|
#include "ED_util.h"
|
|
|
|
#include "GPU_immediate.h"
|
|
#include "GPU_matrix.h"
|
|
#include "GPU_state.h"
|
|
|
|
#include "UI_interface.h"
|
|
#include "UI_resources.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generic Slider
|
|
*
|
|
* The generic slider is supposed to be called during modal operations. It calculates a factor
|
|
* value based on mouse position and draws a visual representation. In order to use it, you need to
|
|
* store a reference to a #tSlider in your operator which you get by calling #ED_slider_create.
|
|
* Then you need to update it during modal operations by calling #ED_slider_modal", which will
|
|
* update #tSlider.factor for you to use. To remove drawing and free the memory, call
|
|
* #ED_slider_destroy.
|
|
* \{ */
|
|
|
|
#define SLIDE_PIXEL_DISTANCE (300.0f * UI_SCALE_FAC)
|
|
#define OVERSHOOT_RANGE_DELTA 0.2f
|
|
|
|
typedef struct tSlider {
|
|
struct Scene *scene;
|
|
struct ScrArea *area;
|
|
|
|
/** Header of the region used for drawing the slider. */
|
|
struct ARegion *region_header;
|
|
|
|
/** Draw callback handler. */
|
|
void *draw_handle;
|
|
|
|
/** Accumulative factor (not clamped or rounded). */
|
|
float raw_factor;
|
|
|
|
/** 0-1 value for determining the influence of whatever is relevant. */
|
|
float factor;
|
|
|
|
/** Last mouse cursor position used for mouse movement delta calculation. */
|
|
float last_cursor[2];
|
|
|
|
/** Allow negative values as well.
|
|
* This is set by the code that uses the slider, as not all operations support
|
|
* negative values. */
|
|
bool is_bidirectional;
|
|
|
|
/** Enable range beyond 0-100%.
|
|
* This is set by the code that uses the slider, as not all operations support
|
|
* extrapolation. */
|
|
bool allow_overshoot;
|
|
|
|
/** Allow overshoot or clamp between 0% and 100%.
|
|
* This is set by the artist while using the slider. */
|
|
bool overshoot;
|
|
|
|
/** Whether keeping CTRL pressed will snap to 10% increments.
|
|
* Default is true. Set to false if the CTRL key is needed for other means. */
|
|
bool allow_increments;
|
|
|
|
/** Move factor in 10% steps. */
|
|
bool increments;
|
|
|
|
/** Reduces factor delta from mouse movement. */
|
|
bool precision;
|
|
|
|
} tSlider;
|
|
|
|
static void draw_overshoot_triangle(const uint8_t color[4],
|
|
const bool facing_right,
|
|
const float x,
|
|
const float y)
|
|
{
|
|
const uint shdr_pos_2d = GPU_vertformat_attr_add(
|
|
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
|
GPU_polygon_smooth(true);
|
|
immUniformColor3ubvAlpha(color, 225);
|
|
const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize;
|
|
const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize;
|
|
|
|
immBegin(GPU_PRIM_TRIS, 3);
|
|
immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y);
|
|
immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2);
|
|
immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2);
|
|
immEnd();
|
|
|
|
GPU_polygon_smooth(false);
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
immUnbindProgram();
|
|
}
|
|
|
|
static void draw_ticks(const float start_factor,
|
|
const float end_factor,
|
|
const float line_start[2],
|
|
const float base_tick_height,
|
|
const float line_width,
|
|
const uint8_t color_overshoot[4],
|
|
const uint8_t color_line[4])
|
|
{
|
|
/* Use factor represented as 0-100 int to avoid floating point precision problems. */
|
|
const int tick_increment = 10;
|
|
|
|
/* Round initial_tick_factor up to the next tick_increment. */
|
|
int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment;
|
|
|
|
while (tick_percentage <= (int)(end_factor * 100)) {
|
|
float tick_height;
|
|
/* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit
|
|
* smaller and the rest is the minimum size. */
|
|
if (tick_percentage % 100 == 0) {
|
|
tick_height = base_tick_height;
|
|
}
|
|
else if (tick_percentage % 50 == 0) {
|
|
tick_height = base_tick_height * 0.8;
|
|
}
|
|
else {
|
|
tick_height = base_tick_height * 0.5;
|
|
}
|
|
|
|
const float x = line_start[0] +
|
|
(((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE;
|
|
const rctf tick_rect = {
|
|
.xmin = x - (line_width / 2),
|
|
.xmax = x + (line_width / 2),
|
|
.ymin = line_start[1] - (tick_height / 2),
|
|
.ymax = line_start[1] + (tick_height / 2),
|
|
};
|
|
|
|
if (tick_percentage < 0 || tick_percentage > 100) {
|
|
UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255);
|
|
}
|
|
else {
|
|
UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255);
|
|
}
|
|
tick_percentage += tick_increment;
|
|
}
|
|
}
|
|
|
|
static void draw_main_line(const rctf *main_line_rect,
|
|
const float factor,
|
|
const bool overshoot,
|
|
const uint8_t color_overshoot[4],
|
|
const uint8_t color_line[4])
|
|
{
|
|
if (overshoot) {
|
|
/* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */
|
|
const float line_zero_percent = main_line_rect->xmin -
|
|
((factor - 0.5f - OVERSHOOT_RANGE_DELTA) *
|
|
SLIDE_PIXEL_DISTANCE);
|
|
|
|
const float clamped_line_zero_percent = clamp_f(
|
|
line_zero_percent, main_line_rect->xmin, main_line_rect->xmax);
|
|
const float clamped_line_hundred_percent = clamp_f(
|
|
line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax);
|
|
|
|
const rctf left_overshoot_line_rect = {
|
|
.xmin = main_line_rect->xmin,
|
|
.xmax = clamped_line_zero_percent,
|
|
.ymin = main_line_rect->ymin,
|
|
.ymax = main_line_rect->ymax,
|
|
};
|
|
const rctf right_overshoot_line_rect = {
|
|
.xmin = clamped_line_hundred_percent,
|
|
.xmax = main_line_rect->xmax,
|
|
.ymin = main_line_rect->ymin,
|
|
.ymax = main_line_rect->ymax,
|
|
};
|
|
UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255);
|
|
UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255);
|
|
|
|
const rctf non_overshoot_line_rect = {
|
|
.xmin = clamped_line_zero_percent,
|
|
.xmax = clamped_line_hundred_percent,
|
|
.ymin = main_line_rect->ymin,
|
|
.ymax = main_line_rect->ymax,
|
|
};
|
|
UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255);
|
|
}
|
|
else {
|
|
UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255);
|
|
}
|
|
}
|
|
|
|
static void draw_backdrop(const int fontid,
|
|
const rctf *main_line_rect,
|
|
const uint8_t color_bg[4],
|
|
const short region_y_size,
|
|
const float base_tick_height)
|
|
{
|
|
float string_pixel_size[2];
|
|
const char *percentage_string_placeholder = "000%%";
|
|
BLF_width_and_height(fontid,
|
|
percentage_string_placeholder,
|
|
sizeof(percentage_string_placeholder),
|
|
&string_pixel_size[0],
|
|
&string_pixel_size[1]);
|
|
const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize};
|
|
const rctf backdrop_rect = {
|
|
.xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0],
|
|
.xmax = main_line_rect->xmax + pad[0],
|
|
.ymin = pad[1],
|
|
.ymax = region_y_size - pad[1],
|
|
};
|
|
UI_draw_roundbox_3ub_alpha(&backdrop_rect, true, 4.0f, color_bg, color_bg[3]);
|
|
}
|
|
|
|
/**
|
|
* Draw an on screen Slider for a Pose Slide Operator.
|
|
*/
|
|
static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void *arg)
|
|
{
|
|
tSlider *slider = arg;
|
|
|
|
/* Only draw in region from which the Operator was started. */
|
|
if (region != slider->region_header) {
|
|
return;
|
|
}
|
|
|
|
uint8_t color_text[4];
|
|
uint8_t color_line[4];
|
|
uint8_t color_handle[4];
|
|
uint8_t color_overshoot[4];
|
|
uint8_t color_bg[4];
|
|
|
|
/* Get theme colors. */
|
|
UI_GetThemeColor4ubv(TH_HEADER_TEXT_HI, color_handle);
|
|
UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_text);
|
|
UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_line);
|
|
UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_overshoot);
|
|
UI_GetThemeColor4ubv(TH_HEADER, color_bg);
|
|
|
|
color_overshoot[0] = color_overshoot[0] * 0.8;
|
|
color_overshoot[1] = color_overshoot[1] * 0.8;
|
|
color_overshoot[2] = color_overshoot[2] * 0.8;
|
|
color_bg[3] = 160;
|
|
|
|
/* Get the default font. */
|
|
const uiStyle *style = UI_style_get();
|
|
const uiFontStyle *fstyle = &style->widget;
|
|
const int fontid = fstyle->uifont_id;
|
|
BLF_color3ubv(fontid, color_text);
|
|
BLF_rotation(fontid, 0.0f);
|
|
|
|
const float line_width = 1.5 * U.pixelsize;
|
|
const float base_tick_height = 12.0 * U.pixelsize;
|
|
const float line_y = region->winy / 2;
|
|
|
|
rctf main_line_rect = {
|
|
.xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2),
|
|
.xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2),
|
|
.ymin = line_y - line_width / 2,
|
|
.ymax = line_y + line_width / 2,
|
|
};
|
|
float line_start_factor = 0;
|
|
int handle_pos_x;
|
|
if (slider->overshoot) {
|
|
main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
|
|
main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA;
|
|
line_start_factor = slider->factor - 0.5f - OVERSHOOT_RANGE_DELTA;
|
|
handle_pos_x = region->winx / 2;
|
|
}
|
|
else if (slider->is_bidirectional) {
|
|
handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * (slider->factor / 2 + 0.5f);
|
|
}
|
|
else {
|
|
handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * slider->factor;
|
|
}
|
|
|
|
draw_backdrop(fontid, &main_line_rect, color_bg, slider->region_header->winy, base_tick_height);
|
|
|
|
draw_main_line(&main_line_rect, slider->factor, slider->overshoot, color_overshoot, color_line);
|
|
|
|
const float factor_range = slider->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1;
|
|
const float line_start_position[2] = {main_line_rect.xmin, line_y};
|
|
draw_ticks(line_start_factor,
|
|
line_start_factor + factor_range,
|
|
line_start_position,
|
|
base_tick_height,
|
|
line_width,
|
|
color_overshoot,
|
|
color_line);
|
|
|
|
/* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100%
|
|
* range. */
|
|
if (slider->overshoot) {
|
|
if (slider->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) {
|
|
draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y);
|
|
}
|
|
if (slider->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) {
|
|
draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y);
|
|
}
|
|
}
|
|
|
|
char percentage_string[256];
|
|
|
|
/* Draw handle indicating current factor. */
|
|
const rctf handle_rect = {
|
|
.xmin = handle_pos_x - (line_width),
|
|
.xmax = handle_pos_x + (line_width),
|
|
.ymin = line_y - (base_tick_height / 2),
|
|
.ymax = line_y + (base_tick_height / 2),
|
|
};
|
|
|
|
UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255);
|
|
BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", slider->factor * 100);
|
|
|
|
/* Draw percentage string. */
|
|
float percentage_string_pixel_size[2];
|
|
BLF_width_and_height(fontid,
|
|
percentage_string,
|
|
sizeof(percentage_string),
|
|
&percentage_string_pixel_size[0],
|
|
&percentage_string_pixel_size[1]);
|
|
|
|
BLF_position(fontid,
|
|
main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2,
|
|
(region->winy / 2) - percentage_string_pixel_size[1] / 2,
|
|
0.0f);
|
|
BLF_draw(fontid, percentage_string, sizeof(percentage_string));
|
|
}
|
|
|
|
static void slider_update_factor(tSlider *slider, const wmEvent *event)
|
|
{
|
|
const float factor_delta = (event->xy[0] - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE;
|
|
/* Reduced factor delta in precision mode (shift held). */
|
|
slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta;
|
|
slider->factor = slider->raw_factor;
|
|
copy_v2fl_v2i(slider->last_cursor, event->xy);
|
|
|
|
if (!slider->overshoot) {
|
|
slider->factor = clamp_f(slider->factor, -1, 1);
|
|
}
|
|
if (!slider->is_bidirectional) {
|
|
slider->factor = max_ff(slider->factor, 0);
|
|
}
|
|
|
|
if (slider->increments) {
|
|
slider->factor = round(slider->factor * 10) / 10;
|
|
}
|
|
}
|
|
|
|
tSlider *ED_slider_create(struct bContext *C)
|
|
{
|
|
tSlider *slider = MEM_callocN(sizeof(tSlider), "tSlider");
|
|
slider->scene = CTX_data_scene(C);
|
|
slider->area = CTX_wm_area(C);
|
|
slider->region_header = CTX_wm_region(C);
|
|
|
|
/* Default is true, caller needs to manually set to false. */
|
|
slider->allow_overshoot = true;
|
|
slider->allow_increments = true;
|
|
|
|
/* Set initial factor. */
|
|
slider->raw_factor = 0.5f;
|
|
slider->factor = 0.5;
|
|
|
|
/* Add draw callback. Always in header. */
|
|
if (slider->area) {
|
|
LISTBASE_FOREACH (ARegion *, region, &slider->area->regionbase) {
|
|
if (region->regiontype == RGN_TYPE_HEADER) {
|
|
slider->region_header = region;
|
|
slider->draw_handle = ED_region_draw_cb_activate(
|
|
region->type, slider_draw, slider, REGION_DRAW_POST_PIXEL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Hide the area menu bar contents, as the slider will be drawn on top. */
|
|
ED_area_status_text(slider->area, "");
|
|
|
|
return slider;
|
|
}
|
|
|
|
void ED_slider_init(struct tSlider *slider, const wmEvent *event)
|
|
{
|
|
copy_v2fl_v2i(slider->last_cursor, event->xy);
|
|
}
|
|
|
|
bool ED_slider_modal(tSlider *slider, const wmEvent *event)
|
|
{
|
|
bool event_handled = true;
|
|
/* Handle key presses. */
|
|
switch (event->type) {
|
|
case EVT_EKEY:
|
|
if (slider->allow_overshoot) {
|
|
slider->overshoot = event->val == KM_PRESS ? !slider->overshoot : slider->overshoot;
|
|
slider_update_factor(slider, event);
|
|
}
|
|
break;
|
|
case EVT_LEFTSHIFTKEY:
|
|
case EVT_RIGHTSHIFTKEY:
|
|
slider->precision = event->val == KM_PRESS;
|
|
break;
|
|
case EVT_LEFTCTRLKEY:
|
|
case EVT_RIGHTCTRLKEY:
|
|
slider->increments = slider->allow_increments && event->val == KM_PRESS;
|
|
break;
|
|
case MOUSEMOVE:;
|
|
/* Update factor. */
|
|
slider_update_factor(slider, event);
|
|
break;
|
|
default:
|
|
event_handled = false;
|
|
break;
|
|
}
|
|
|
|
ED_region_tag_redraw(slider->region_header);
|
|
|
|
return event_handled;
|
|
}
|
|
|
|
void ED_slider_status_string_get(const struct tSlider *slider,
|
|
char *status_string,
|
|
const size_t size_of_status_string)
|
|
{
|
|
/* 50 characters is enough to fit the individual setting strings. Extend if message is longer. */
|
|
char overshoot_str[50];
|
|
char precision_str[50];
|
|
char increments_str[50];
|
|
|
|
if (slider->allow_overshoot) {
|
|
if (slider->overshoot) {
|
|
STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot"));
|
|
}
|
|
else {
|
|
STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot"));
|
|
}
|
|
}
|
|
else {
|
|
STRNCPY(overshoot_str, TIP_("Overshoot disabled"));
|
|
}
|
|
|
|
if (slider->precision) {
|
|
STRNCPY(precision_str, TIP_("[Shift] - Precision active"));
|
|
}
|
|
else {
|
|
STRNCPY(precision_str, TIP_("Shift - Hold for precision"));
|
|
}
|
|
|
|
if (slider->allow_increments) {
|
|
if (slider->increments) {
|
|
STRNCPY(increments_str, TIP_(" | [Ctrl] - Increments active"));
|
|
}
|
|
else {
|
|
STRNCPY(increments_str, TIP_(" | Ctrl - Hold for 10% increments"));
|
|
}
|
|
}
|
|
else {
|
|
increments_str[0] = '\0';
|
|
}
|
|
|
|
BLI_snprintf(status_string,
|
|
size_of_status_string,
|
|
"%s | %s%s",
|
|
overshoot_str,
|
|
precision_str,
|
|
increments_str);
|
|
}
|
|
|
|
void ED_slider_destroy(struct bContext *C, tSlider *slider)
|
|
{
|
|
/* Remove draw callback. */
|
|
if (slider->draw_handle) {
|
|
ED_region_draw_cb_exit(slider->region_header->type, slider->draw_handle);
|
|
}
|
|
ED_area_status_text(slider->area, NULL);
|
|
ED_workspace_status_text(C, NULL);
|
|
MEM_freeN(slider);
|
|
}
|
|
|
|
/* Setters & Getters */
|
|
|
|
float ED_slider_factor_get(struct tSlider *slider)
|
|
{
|
|
return slider->factor;
|
|
}
|
|
|
|
void ED_slider_factor_set(struct tSlider *slider, const float factor)
|
|
{
|
|
slider->raw_factor = factor;
|
|
slider->factor = factor;
|
|
if (!slider->overshoot) {
|
|
slider->factor = clamp_f(slider->factor, 0, 1);
|
|
}
|
|
}
|
|
|
|
bool ED_slider_allow_overshoot_get(struct tSlider *slider)
|
|
{
|
|
return slider->allow_overshoot;
|
|
}
|
|
|
|
void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value)
|
|
{
|
|
slider->allow_overshoot = value;
|
|
}
|
|
|
|
bool ED_slider_allow_increments_get(struct tSlider *slider)
|
|
{
|
|
return slider->allow_increments;
|
|
}
|
|
|
|
void ED_slider_allow_increments_set(struct tSlider *slider, const bool value)
|
|
{
|
|
slider->allow_increments = value;
|
|
}
|
|
|
|
bool ED_slider_is_bidirectional_get(struct tSlider *slider)
|
|
{
|
|
return slider->is_bidirectional;
|
|
}
|
|
|
|
void ED_slider_is_bidirectional_set(struct tSlider *slider, const bool value)
|
|
{
|
|
slider->is_bidirectional = value;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_info)
|
|
{
|
|
wmWindow *win = CTX_wm_window(C);
|
|
const float *mval_src = (float *)arg_info;
|
|
const float mval_dst[2] = {
|
|
win->eventstate->xy[0] - region->winrct.xmin,
|
|
win->eventstate->xy[1] - region->winrct.ymin,
|
|
};
|
|
|
|
const uint shdr_pos = GPU_vertformat_attr_add(
|
|
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
GPU_line_width(1.0f);
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
|
|
|
|
float viewport_size[4];
|
|
GPU_viewport_size_get_f(viewport_size);
|
|
immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
|
|
|
|
immUniform1i("colors_len", 0); /* "simple" mode */
|
|
immUniformThemeColor3(TH_VIEW_OVERLAY);
|
|
immUniform1f("dash_width", 6.0f);
|
|
immUniform1f("udash_factor", 0.5f);
|
|
|
|
immBegin(GPU_PRIM_LINES, 2);
|
|
immVertex2fv(shdr_pos, mval_src);
|
|
immVertex2fv(shdr_pos, mval_dst);
|
|
immEnd();
|
|
|
|
immUnbindProgram();
|
|
}
|
|
|
|
#define MAX_METADATA_STR 1024
|
|
|
|
static const char *meta_data_list[] = {
|
|
"File",
|
|
"Strip",
|
|
"Date",
|
|
"RenderTime",
|
|
"Note",
|
|
"Marker",
|
|
"Time",
|
|
"Frame",
|
|
"Camera",
|
|
"Scene",
|
|
};
|
|
|
|
BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset)
|
|
{
|
|
return (IMB_metadata_get_field(
|
|
ibuf->metadata, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) &&
|
|
r_str[0]);
|
|
}
|
|
|
|
BLI_INLINE bool metadata_is_custom_drawable(const char *field)
|
|
{
|
|
/* Metadata field stored by Blender for multi-layer EXR images. Is rather
|
|
* useless to be viewed all the time. Can still be seen in the Metadata
|
|
* panel. */
|
|
if (STREQ(field, "BlenderMultiChannel")) {
|
|
return false;
|
|
}
|
|
/* Is almost always has value "scanlineimage", also useless to be seen
|
|
* all the time. */
|
|
if (STREQ(field, "type")) {
|
|
return false;
|
|
}
|
|
return !BKE_stamp_is_known_field(field);
|
|
}
|
|
|
|
typedef struct MetadataCustomDrawContext {
|
|
int fontid;
|
|
int xmin, ymin;
|
|
int vertical_offset;
|
|
int current_y;
|
|
} MetadataCustomDrawContext;
|
|
|
|
static void metadata_custom_draw_fields(const char *field, const char *value, void *ctx_v)
|
|
{
|
|
if (!metadata_is_custom_drawable(field)) {
|
|
return;
|
|
}
|
|
MetadataCustomDrawContext *ctx = (MetadataCustomDrawContext *)ctx_v;
|
|
char temp_str[MAX_METADATA_STR];
|
|
SNPRINTF(temp_str, "%s: %s", field, value);
|
|
BLF_position(ctx->fontid, ctx->xmin, ctx->ymin + ctx->current_y, 0.0f);
|
|
BLF_draw(ctx->fontid, temp_str, sizeof(temp_str));
|
|
ctx->current_y += ctx->vertical_offset;
|
|
}
|
|
|
|
static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top)
|
|
{
|
|
char temp_str[MAX_METADATA_STR];
|
|
int ofs_y = 0;
|
|
const float height = BLF_height_max(fontid);
|
|
const float margin = height / 8;
|
|
const float vertical_offset = (height + margin);
|
|
|
|
/* values taking margins into account */
|
|
const float descender = BLF_descender(fontid);
|
|
const float xmin = (rect->xmin + margin);
|
|
const float xmax = (rect->xmax - margin);
|
|
const float ymin = (rect->ymin + margin) - descender;
|
|
const float ymax = (rect->ymax - margin) - descender;
|
|
|
|
if (is_top) {
|
|
for (int i = 0; i < 4; i++) {
|
|
/* first line */
|
|
if (i == 0) {
|
|
bool do_newline = false;
|
|
int len = SNPRINTF_RLEN(temp_str, "%s: ", meta_data_list[0]);
|
|
if (metadata_is_valid(ibuf, temp_str, 0, len)) {
|
|
BLF_position(fontid, xmin, ymax - vertical_offset, 0.0f);
|
|
BLF_draw(fontid, temp_str, sizeof(temp_str));
|
|
do_newline = true;
|
|
}
|
|
|
|
len = SNPRINTF_RLEN(temp_str, "%s: ", meta_data_list[1]);
|
|
if (metadata_is_valid(ibuf, temp_str, 1, len)) {
|
|
int line_width = BLF_width(fontid, temp_str, sizeof(temp_str));
|
|
BLF_position(fontid, xmax - line_width, ymax - vertical_offset, 0.0f);
|
|
BLF_draw(fontid, temp_str, sizeof(temp_str));
|
|
do_newline = true;
|
|
}
|
|
|
|
if (do_newline) {
|
|
ofs_y += vertical_offset;
|
|
}
|
|
} /* Strip */
|
|
else if (ELEM(i, 1, 2)) {
|
|
int len = SNPRINTF_RLEN(temp_str, "%s: ", meta_data_list[i + 1]);
|
|
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
|
|
BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
|
|
BLF_draw(fontid, temp_str, sizeof(temp_str));
|
|
ofs_y += vertical_offset;
|
|
}
|
|
} /* Note (wrapped) */
|
|
else if (i == 3) {
|
|
int len = SNPRINTF_RLEN(temp_str, "%s: ", meta_data_list[i + 1]);
|
|
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
|
|
struct ResultBLF info;
|
|
BLF_enable(fontid, BLF_WORD_WRAP);
|
|
BLF_wordwrap(fontid, ibuf->x - (margin * 2));
|
|
BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f);
|
|
BLF_draw_ex(fontid, temp_str, sizeof(temp_str), &info);
|
|
BLF_wordwrap(fontid, 0);
|
|
BLF_disable(fontid, BLF_WORD_WRAP);
|
|
ofs_y += vertical_offset * info.lines;
|
|
}
|
|
}
|
|
else {
|
|
int len = SNPRINTF_RLEN(temp_str, "%s: ", meta_data_list[i + 1]);
|
|
if (metadata_is_valid(ibuf, temp_str, i + 1, len)) {
|
|
int line_width = BLF_width(fontid, temp_str, sizeof(temp_str));
|
|
BLF_position(fontid, xmax - line_width, ymax - vertical_offset - ofs_y, 0.0f);
|
|
BLF_draw(fontid, temp_str, sizeof(temp_str));
|
|
ofs_y += vertical_offset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
MetadataCustomDrawContext ctx;
|
|
ctx.fontid = fontid;
|
|
ctx.xmin = xmin;
|
|
ctx.ymin = ymin;
|
|
ctx.current_y = ofs_y;
|
|
ctx.vertical_offset = vertical_offset;
|
|
IMB_metadata_foreach(ibuf, metadata_custom_draw_fields, &ctx);
|
|
int ofs_x = 0;
|
|
ofs_y = ctx.current_y;
|
|
for (int i = 5; i < 10; i++) {
|
|
int len = SNPRINTF_RLEN(temp_str, "%s: ", meta_data_list[i]);
|
|
if (metadata_is_valid(ibuf, temp_str, i, len)) {
|
|
BLF_position(fontid, xmin + ofs_x, ymin + ofs_y, 0.0f);
|
|
BLF_draw(fontid, temp_str, sizeof(temp_str));
|
|
|
|
ofs_x += BLF_width(fontid, temp_str, sizeof(temp_str)) + UI_UNIT_X;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct MetadataCustomCountContext {
|
|
int count;
|
|
} MetadataCustomCountContext;
|
|
|
|
static void metadata_custom_count_fields(const char *field, const char *UNUSED(value), void *ctx_v)
|
|
{
|
|
if (!metadata_is_custom_drawable(field)) {
|
|
return;
|
|
}
|
|
MetadataCustomCountContext *ctx = (MetadataCustomCountContext *)ctx_v;
|
|
ctx->count++;
|
|
}
|
|
|
|
static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top)
|
|
{
|
|
const float height = BLF_height_max(fontid);
|
|
const float margin = (height / 8);
|
|
char str[MAX_METADATA_STR] = "";
|
|
short count = 0;
|
|
|
|
if (is_top) {
|
|
if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) {
|
|
count++;
|
|
}
|
|
for (int i = 2; i < 5; i++) {
|
|
if (metadata_is_valid(ibuf, str, i, 0)) {
|
|
if (i == 4) {
|
|
struct {
|
|
struct ResultBLF info;
|
|
rcti rect;
|
|
} wrap;
|
|
|
|
BLF_enable(fontid, BLF_WORD_WRAP);
|
|
BLF_wordwrap(fontid, ibuf->x - (margin * 2));
|
|
BLF_boundbox_ex(fontid, str, sizeof(str), &wrap.rect, &wrap.info);
|
|
BLF_wordwrap(fontid, 0);
|
|
BLF_disable(fontid, BLF_WORD_WRAP);
|
|
|
|
count += wrap.info.lines;
|
|
}
|
|
else {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (int i = 5; i < 10; i++) {
|
|
if (metadata_is_valid(ibuf, str, i, 0)) {
|
|
count = 1;
|
|
break;
|
|
}
|
|
}
|
|
MetadataCustomCountContext ctx;
|
|
ctx.count = 0;
|
|
IMB_metadata_foreach(ibuf, metadata_custom_count_fields, &ctx);
|
|
count += ctx.count;
|
|
}
|
|
|
|
if (count) {
|
|
return (height + margin) * count;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ED_region_image_metadata_draw(
|
|
int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy)
|
|
{
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
|
|
if (!ibuf->metadata) {
|
|
return;
|
|
}
|
|
|
|
/* find window pixel coordinates of origin */
|
|
GPU_matrix_push();
|
|
|
|
/* Offset and zoom using GPU viewport. */
|
|
GPU_matrix_translate_2f(x, y);
|
|
GPU_matrix_scale_2f(zoomx, zoomy);
|
|
|
|
BLF_size(blf_mono_font, style->widgetlabel.points * UI_SCALE_FAC);
|
|
|
|
/* *** upper box*** */
|
|
|
|
/* get needed box height */
|
|
float box_y = metadata_box_height_get(ibuf, blf_mono_font, true);
|
|
|
|
if (box_y) {
|
|
/* set up rect */
|
|
rctf rect;
|
|
BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y);
|
|
/* draw top box */
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
|
immUniformThemeColor(TH_METADATA_BG);
|
|
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
|
|
immUnbindProgram();
|
|
|
|
BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
|
|
BLF_enable(blf_mono_font, BLF_CLIPPING);
|
|
|
|
UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
|
|
metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true);
|
|
|
|
BLF_disable(blf_mono_font, BLF_CLIPPING);
|
|
}
|
|
|
|
/* *** lower box*** */
|
|
|
|
box_y = metadata_box_height_get(ibuf, blf_mono_font, false);
|
|
|
|
if (box_y) {
|
|
/* set up box rect */
|
|
rctf rect;
|
|
BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin);
|
|
/* draw top box */
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
|
immUniformThemeColor(TH_METADATA_BG);
|
|
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
|
|
immUnbindProgram();
|
|
|
|
BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
|
|
BLF_enable(blf_mono_font, BLF_CLIPPING);
|
|
|
|
UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT);
|
|
metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false);
|
|
|
|
BLF_disable(blf_mono_font, BLF_CLIPPING);
|
|
}
|
|
|
|
GPU_matrix_pop();
|
|
}
|
|
|
|
#undef MAX_METADATA_STR
|