UI: File Browser Design Overhaul

This is a general redesign of the File Browser GUI and interaction
methods. For screenshots, check patch D5601.

Main changes in short:
* File Browser as floating window
* New layout of regions
* Popovers for view and filter options
* Vertical list view with interactive column header
* New and updated icons
* Keymap consistency fixes
* Many tweaks and fixes to the drawing of views

----

General:
* The file browser now opens as temporary floating window. It closes on
  Esc. The header is hidden then.
* When the file browser is opened as regular editor, the header remains
  visible.
* All file browser regions are now defined in Python (the button
  layout).
* Adjusted related operator UI names.

Keymap:
Keymap is now consistent with other list-based views in Blender, such as
the Outliner.
* Left click to select, double-click to open
* Right-click context menus
* Shift-click to fill selection
* Ctrl-click to extend selection

Operator options:
These previously overlapped with the source list, which caused numerous
issues with resizing and presenting many settings in a small panel area.
It was also generally inconsistent with Blender.
* Moved to new sidebar, which can easily be shown or hidden using a
  prominent Options toggle.
* IO operators have new layouts to match this new sidebar, using
  sub-panels. This will have to be committed separately (Add-on
  repository).
* If operators want to show the options by default, they have the option
  to do so (see `WM_FILESEL_SHOW_PROPS`, `hide_props_region`), otherwise
  they are hidden by default.

General Layout:
The layout has been changed to be simpler, more standard, and fits
better in with Blender 2.8.
* More conventional layout (file path at top, file name at the bottom,
  execute/cancel buttons in bottom right).
* Use of popovers to group controls, and allow for more descriptive
  naming.
* Search box is always live now, just like Outliner.

Views:
* Date Modified column combines both date and time, also uses user
  friendly strings for recent dates (i.e. "Yesterday", "Today").
* Details columns (file size, modification date/time) are now toggleable
  for all display types, they are not hardcoded per display type.
* File sizes now show as B, KB, MB, ... rather than B, KiB, MiB, … They
  are now also calculated using base 10 of course.
* Option to sort in inverse order.

Vertical List View:
* This view now used a much simpler single vertical list with columns
  for information.
* Users can click on the headers of these columns to order by that
  category, and click again to reverse the ordering.

Icons:
* Updated icons by Jendrzych, with better centering.
* Files and folders have new icons in Icon view.
* Both files and folders have reworked superimposed icons that show
  users the file/folder type.
* 3D file documents correctly use the 3d file icon, which was unused
  previously.
* Workspaces now show their icon on Link/Append - also when listed in
  the Outliner.

Minor Python-API breakage:
* `bpy.types.FileSelectParams.display_type`: `LIST_SHORT` and
  `LIST_LONG` are replaced by `LIST_VERTICAL` and `LIST_HORIZONTAL`.

Removes the feature where directories would automatically be created if
they are entered into the file path text button, but don't exist. We
were not sure if users use it enough to keep it. We can definitely bring
it back.

----

//Combined effort by @billreynish, @harley, @jendrzych, my university
colleague Brian Meisenheimer and myself.//

Differential Revision: https://developer.blender.org/D5601

Reviewers: Brecht, Bastien
This commit is contained in:
Julian Eisel 2019-09-03 15:43:38 +02:00
parent 545f565426
commit ee8f69c96c
Notes: blender-bot 2023-03-24 17:05:22 +01:00
Referenced by issue #102414, BLI_assert While Splitting File Browser
Referenced by issue #97142, Regression: When Exporting Grease Pencil, File Dialog Shows No Subfolders
Referenced by issue #69495, Crash after pressing ctrl+s in file view
Referenced by issue #69469, Address Sanitizer reports read overrun in blender/windowmanager/intern/wm_operator_props.c
33 changed files with 1900 additions and 1584 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 562 KiB

After

Width:  |  Height:  |  Size: 543 KiB

48
release/datafiles/prvicons_update.py Executable file → Normal file
View File

@ -1,23 +1,25 @@
#!/usr/bin/env python3
# This script updates icons from the SVG file
import os
import subprocess
import sys
BASEDIR = os.path.abspath(os.path.dirname(__file__))
inkscape_path = 'inkscape'
if sys.platform == 'darwin':
inkscape_app_path = '/Applications/Inkscape.app/Contents/Resources/script'
if os.path.exists(inkscape_app_path):
inkscape_path = inkscape_app_path
cmd = (
inkscape_path,
os.path.join(BASEDIR, "prvicons.svg"),
"--without-gui",
"--export-png=" + os.path.join(BASEDIR, "prvicons.png"),
)
subprocess.check_call(cmd)
#!/usr/bin/env python3
# This script updates icons from the SVG file
import os
import subprocess
import sys
BASEDIR = os.path.abspath(os.path.dirname(__file__))
inkscape_path = 'inkscape'
if sys.platform == 'darwin':
inkscape_app_path = '/Applications/Inkscape.app/Contents/Resources/script'
if os.path.exists(inkscape_app_path):
inkscape_path = inkscape_app_path
cmd = (
inkscape_path,
os.path.join(BASEDIR, "prvicons.svg"),
"--export-width=1536",
"--export-height=256",
"--without-gui",
"--export-png=" + os.path.join(BASEDIR, "prvicons.png"),
)
subprocess.check_call(cmd)

View File

@ -370,7 +370,7 @@ const bTheme U_theme_default = {
.paint_curve_handle = RGBA(0x7fff7f7f),
},
.space_file = {
.back = RGBA(0x35353500),
.back = RGBA(0x33333300),
.title = RGBA(0xffffffff),
.text = RGBA(0xe6e6e6ff),
.text_hi = RGBA(0xffffffff),
@ -381,13 +381,14 @@ const bTheme U_theme_default = {
.tab_inactive = RGBA(0x2b2b2bff),
.tab_back = RGBA(0x232323ff),
.tab_outline = RGBA(0x232323ff),
.button = RGBA(0x424242ff),
.button = RGBA(0x4b4b4bff),
.button_title = RGBA(0xffffffff),
.button_text = RGBA(0xe5e5e5ff),
.button_text_hi = RGBA(0xffffffff),
.execution_buts = RGBA(0x444444ff),
.panelcolors = {
.header = RGBA(0x424242cc),
.back = RGBA(0x333333b3),
.header = RGBA(0x4b4b4bff),
.back = RGBA(0x404040ff),
.sub_back = RGBA(0x0000003e),
},
.hilite = RGBA(0x4f76b3ff),

View File

@ -1771,6 +1771,11 @@ def km_file_browser(_params):
)
items.extend([
*_template_space_region_type_toggle(
toolbar_key={"type": 'T', "value": 'PRESS'},
),
("screen.region_toggle", {"type": 'N', "value": 'PRESS'},
{"properties": [("region_type", 'TOOL_PROPS')]}),
("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True}, None),
("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True}, None),
("file.next", {"type": 'RIGHT_ARROW', "value": 'PRESS', "alt": True}, None),
@ -1782,7 +1787,6 @@ def km_file_browser(_params):
{"properties": [("data_path", 'space_data.params.show_hidden')]}),
("file.directory_new", {"type": 'I', "value": 'PRESS'}, None),
("file.smoothscroll", {"type": 'TIMER1', "value": 'ANY', "any": True}, None),
("file.bookmark_toggle", {"type": 'T', "value": 'PRESS'}, None),
("file.bookmark_add", {"type": 'B', "value": 'PRESS', "ctrl": True}, None),
])
@ -1801,17 +1805,13 @@ def km_file_browser_main(params):
("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
{"properties": [("need_active", True)]}),
("file.refresh", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "ctrl": True},
{"properties": [("extend", True), ("fill", True)]}),
("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK'},
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'},
{"properties": [("open", False)]}),
("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True), ("open", False)]}),
("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK', "alt": True},
{"properties": [("extend", True), ("fill", True), ("open", False)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
{"properties": [("extend", True)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
{"properties": [("extend", True), ("fill", True)]}),
("file.select_walk", {"type": 'UP_ARROW', "value": 'PRESS'},
{"properties": [("direction", 'UP')]}),
("file.select_walk", {"type": 'UP_ARROW', "value": 'PRESS', "shift": True},
@ -1843,8 +1843,11 @@ def km_file_browser_main(params):
("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
{"properties": [("mode", 'ADD')]}),
("file.rename", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "ctrl": True},
{"properties": [("mode", 'SUB')]}),
("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
op_menu("FILEBROWSER_MT_context_menu", params.context_menu_event),
("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS'},
{"properties": [("increment", 1)]}),
("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True},

View File

@ -1196,10 +1196,11 @@ def km_file_browser_main(params):
("file.execute", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
{"properties": [("need_active", True)]}),
("file.refresh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True},
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK'},
{"properties": [("open", False)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True},
{"properties": [("extend", True)]}),
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True, "ctrl": True},
("file.select", {"type": 'LEFTMOUSE', "value": 'CLICK', "shift": True,},
{"properties": [("extend", True), ("fill", True)]}),
("file.select", {"type": 'RIGHTMOUSE', "value": 'CLICK'},
{"properties": [("open", False)]}),
@ -1239,6 +1240,8 @@ def km_file_browser_main(params):
("file.select_box", {"type": 'EVT_TWEAK_L', "value": 'ANY', "shift": True},
{"properties": [("mode", 'ADD')]}),
("file.highlight", {"type": 'MOUSEMOVE', "value": 'ANY', "any": True}, None),
("file.sort_column_ui_context", {"type": 'LEFTMOUSE', "value": 'PRESS', "any": True}, None),
op_menu("FILEBROWSER_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS'},
{"properties": [("increment", 1)]}),
("file.filenum", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "shift": True},

View File

@ -34,58 +34,131 @@ class FILEBROWSER_HT_header(Header):
layout.menu("FILEBROWSER_MT_view")
row = layout.row(align=True)
row.operator("file.previous", text="", icon='BACK')
row.operator("file.next", text="", icon='FORWARD')
row.operator("file.parent", text="", icon='FILE_PARENT')
row.operator("file.refresh", text="", icon='FILE_REFRESH')
layout.operator_context = 'EXEC_DEFAULT'
layout.operator("file.directory_new", icon='NEWFOLDER', text="")
layout.operator_context = 'INVOKE_DEFAULT'
# can be None when save/reload with a file selector open
if params:
is_lib_browser = params.use_library_browsing
layout.prop(params, "display_type", expand=True, text="")
layout.prop(params, "sort_method", expand=True, text="")
layout.prop(params, "show_hidden", text="", icon='FILE_HIDDEN')
layout.separator_spacer()
layout.template_running_jobs()
if params:
layout.prop(params, "use_filter", text="", icon='FILTER')
row = layout.row(align=True)
row.active = params.use_filter
row.prop(params, "use_filter_folder", text="")
class FILEBROWSER_PT_display(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'HEADER'
bl_label = "Display"
if params.filter_glob:
# if st.active_operator and hasattr(st.active_operator, "filter_glob"):
# row.prop(params, "filter_glob", text="")
row.label(text=params.filter_glob)
else:
row.prop(params, "use_filter_blender", text="")
row.prop(params, "use_filter_backup", text="")
row.prop(params, "use_filter_image", text="")
row.prop(params, "use_filter_movie", text="")
row.prop(params, "use_filter_script", text="")
row.prop(params, "use_filter_font", text="")
row.prop(params, "use_filter_sound", text="")
row.prop(params, "use_filter_text", text="")
@classmethod
def poll(cls, context):
# can be None when save/reload with a file selector open
return context.space_data.params is not None
if is_lib_browser:
row.prop(params, "use_filter_blendid", text="")
if params.use_filter_blendid:
row.separator()
row.prop(params, "filter_id_category", text="")
def draw(self, context):
layout = self.layout
row.separator()
row.prop(params, "filter_search", text="", icon='VIEWZOOM')
space = context.space_data
params = space.params
is_lib_browser = params.use_library_browsing
layout.label(text="Display as")
layout.column().prop(params, "display_type", expand=True)
layout.use_property_split = True
layout.use_property_decorate = False # No animation.
if params.display_type == 'THUMBNAIL':
layout.prop(params, "display_size", text="Size")
else:
layout.prop(params, "show_details_size", text="Size")
layout.prop(params, "show_details_datetime", text="Date")
layout.prop(params, "recursion_level", text="Recursions")
layout.use_property_split = False
layout.separator()
layout.label(text="Sort by")
layout.column().prop(params, "sort_method", expand=True)
layout.prop(params, "use_sort_invert")
class FILEBROWSER_PT_filter(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'HEADER'
bl_label = "Filter"
@classmethod
def poll(cls, context):
# can be None when save/reload with a file selector open
return context.space_data.params is not None
def draw(self, context):
layout = self.layout
space = context.space_data
params = space.params
is_lib_browser = params.use_library_browsing
row = layout.row(align=True)
row.prop(params, "use_filter", text="", toggle=0)
row.label(text="Filter")
col = layout.column()
col.active = params.use_filter
row = col.row()
row.label(icon='FILE_FOLDER')
row.prop(params, "use_filter_folder", text="Folders", toggle=0)
if params.filter_glob:
col.label(text=params.filter_glob)
else:
row = col.row()
row.label(icon='FILE_BLEND')
row.prop(params, "use_filter_blender",
text=".blend Files", toggle=0)
row = col.row()
row.label(icon='FILE_BACKUP')
row.prop(params, "use_filter_backup",
text="Backup .blend Files", toggle=0)
row = col.row()
row.label(icon='FILE_IMAGE')
row.prop(params, "use_filter_image", text="Image Files", toggle=0)
row = col.row()
row.label(icon='FILE_MOVIE')
row.prop(params, "use_filter_movie", text="Movie Files", toggle=0)
row = col.row()
row.label(icon='FILE_SCRIPT')
row.prop(params, "use_filter_script",
text="Script Files", toggle=0)
row = col.row()
row.label(icon='FILE_FONT')
row.prop(params, "use_filter_font", text="Font Files", toggle=0)
row = col.row()
row.label(icon='FILE_SOUND')
row.prop(params, "use_filter_sound", text="Sound Files", toggle=0)
row = col.row()
row.label(icon='FILE_TEXT')
row.prop(params, "use_filter_text", text="Text Files", toggle=0)
col.separator()
if is_lib_browser:
row = col.row()
row.label(icon='BLANK1') # Indentation
row.prop(params, "use_filter_blendid",
text="Blender IDs", toggle=0)
if params.use_filter_blendid:
row = col.row()
row.label(icon='BLANK1') # Indentation
row.prop(params, "filter_id_category", text="")
col.separator()
layout.prop(params, "show_hidden")
def panel_poll_is_upper_region(region):
# The upper region is left-aligned, the lower is split into it then.
return region.alignment == 'LEFT'
class FILEBROWSER_UL_dir(UIList):
@ -119,10 +192,13 @@ class FILEBROWSER_UL_dir(UIList):
class FILEBROWSER_PT_bookmarks_volumes(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOLS'
bl_options = {'DEFAULT_CLOSED'}
bl_category = "Bookmarks"
bl_label = "Volumes"
@classmethod
def poll(cls, context):
return panel_poll_is_upper_region(context.region)
def draw(self, context):
layout = self.layout
space = context.space_data
@ -141,7 +217,7 @@ class FILEBROWSER_PT_bookmarks_system(Panel):
@classmethod
def poll(cls, context):
return not context.preferences.filepaths.hide_system_bookmarks
return not context.preferences.filepaths.hide_system_bookmarks and panel_poll_is_upper_region(context.region)
def draw(self, context):
layout = self.layout
@ -161,8 +237,10 @@ class FILEBROWSER_MT_bookmarks_context_menu(Menu):
layout.operator("file.bookmark_cleanup", icon='X', text="Cleanup")
layout.separator()
layout.operator("file.bookmark_move", icon='TRIA_UP_BAR', text="Move To Top").direction = 'TOP'
layout.operator("file.bookmark_move", icon='TRIA_DOWN_BAR', text="Move To Bottom").direction = 'BOTTOM'
layout.operator("file.bookmark_move", icon='TRIA_UP_BAR',
text="Move To Top").direction = 'TOP'
layout.operator("file.bookmark_move", icon='TRIA_DOWN_BAR',
text="Move To Bottom").direction = 'BOTTOM'
class FILEBROWSER_PT_bookmarks_favorites(Panel):
@ -171,6 +249,10 @@ class FILEBROWSER_PT_bookmarks_favorites(Panel):
bl_category = "Bookmarks"
bl_label = "Favorites"
@classmethod
def poll(cls, context):
return panel_poll_is_upper_region(context.region)
def draw(self, context):
layout = self.layout
space = context.space_data
@ -185,12 +267,15 @@ class FILEBROWSER_PT_bookmarks_favorites(Panel):
col = row.column(align=True)
col.operator("file.bookmark_add", icon='ADD', text="")
col.operator("file.bookmark_delete", icon='REMOVE', text="")
col.menu("FILEBROWSER_MT_bookmarks_context_menu", icon='DOWNARROW_HLT', text="")
col.menu("FILEBROWSER_MT_bookmarks_context_menu",
icon='DOWNARROW_HLT', text="")
if num_rows > 1:
col.separator()
col.operator("file.bookmark_move", icon='TRIA_UP', text="").direction = 'UP'
col.operator("file.bookmark_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
col.operator("file.bookmark_move", icon='TRIA_UP',
text="").direction = 'UP'
col.operator("file.bookmark_move", icon='TRIA_DOWN',
text="").direction = 'DOWN'
else:
layout.operator("file.bookmark_add", icon='ADD')
@ -203,7 +288,7 @@ class FILEBROWSER_PT_bookmarks_recents(Panel):
@classmethod
def poll(cls, context):
return not context.preferences.filepaths.hide_recent_locations
return not context.preferences.filepaths.hide_recent_locations and panel_poll_is_upper_region(context.region)
def draw(self, context):
layout = self.layout
@ -227,7 +312,7 @@ class FILEBROWSER_PT_advanced_filter(Panel):
@classmethod
def poll(cls, context):
# only useful in append/link (library) context currently...
return context.space_data.params.use_library_browsing
return context.space_data.params.use_library_browsing and panel_poll_is_upper_region(context.region)
def draw(self, context):
layout = self.layout
@ -242,6 +327,133 @@ class FILEBROWSER_PT_advanced_filter(Panel):
col.prop(params, "filter_id")
class FILEBROWSER_PT_options_toggle(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOLS'
bl_label = "Options Toggle"
bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
sfile = context.space_data
return context.region.alignment == 'BOTTOM' and sfile.active_operator
def is_option_region_visible(self, context):
for region in context.area.regions:
if region.type == 'TOOL_PROPS' and region.width <= 1:
return False
return True
def draw(self, context):
layout = self.layout
label = "Hide Options" if self.is_option_region_visible(
context) else "Options"
layout.scale_x = 1.3
layout.scale_y = 1.3
layout.operator("screen.region_toggle",
text=label).region_type = 'TOOL_PROPS'
class FILEBROWSER_PT_directory_path(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'UI'
bl_label = "Directory Path"
bl_category = "Attributes"
bl_options = {'HIDE_HEADER'}
def is_header_visible(self, context):
for region in context.area.regions:
if region.type == 'HEADER' and region.height <= 1:
return False
return True
def draw(self, context):
layout = self.layout
space = context.space_data
params = space.params
layout.scale_x = 1.3
layout.scale_y = 1.3
row = layout.row()
subrow = row.row(align=True)
subrow.operator("file.previous", text="", icon='BACK')
subrow.operator("file.next", text="", icon='FORWARD')
subrow.operator("file.parent", text="", icon='FILE_PARENT')
subrow.operator("file.refresh", text="", icon='FILE_REFRESH')
row.operator("file.directory_new", icon='NEWFOLDER', text="")
subrow = row.row()
subrow.prop(params, "directory", text="")
subrow = row.row()
subrow.scale_x = 0.5
subrow.prop(params, "filter_search", text="", icon='VIEWZOOM')
# Uses prop_with_popover() as popover() only adds the triangle icon in headers.
row.prop_with_popover(
params,
"display_type",
panel="FILEBROWSER_PT_display",
text="",
icon_only=True,
)
row.prop_with_popover(
params,
"display_type",
panel="FILEBROWSER_PT_filter",
text="",
icon='FILTER',
icon_only=True,
)
class FILEBROWSER_PT_file_operation(Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'EXECUTE'
bl_label = "Execute File Operation"
bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
return context.space_data.active_operator
def draw(self, context):
import sys
layout = self.layout
space = context.space_data
params = space.params
layout.scale_x = 1.3
layout.scale_y = 1.3
row = layout.row()
sub = row.row()
sub.prop(params, "filename", text="")
sub = row.row()
sub.ui_units_x = 5
# subsub = sub.row(align=True)
# subsub.operator("file.filenum", text="", icon='ADD').increment = 1
# subsub.operator("file.filenum", text="", icon='REMOVE').increment = -1
# organize buttons according to the OS standard
if sys.platform != "win":
sub.operator("FILE_OT_cancel", text="Cancel")
subsub = sub.row()
subsub.active_default = True
subsub.operator("FILE_OT_execute", text=params.title)
if sys.platform == "win":
sub.operator("FILE_OT_cancel", text="Cancel")
class FILEBROWSER_MT_view(Menu):
bl_label = "View"
@ -250,7 +462,7 @@ class FILEBROWSER_MT_view(Menu):
st = context.space_data
params = st.params
layout.prop(st, "show_region_toolbar")
layout.prop(st, "show_region_toolbar", text="Source List")
layout.prop(st, "show_region_ui", text="File Path")
layout.separator()
@ -263,8 +475,46 @@ class FILEBROWSER_MT_view(Menu):
layout.menu("INFO_MT_area")
class FILEBROWSER_MT_context_menu(Menu):
bl_label = "Files Context Menu"
def draw(self, context):
layout = self.layout
st = context.space_data
params = st.params
layout.operator("file.previous", text="Back")
layout.operator("file.next", text="Forward")
layout.operator("file.parent", text="Go to Parent")
layout.operator("file.refresh", text="Refresh")
layout.separator()
layout.operator("file.filenum", text="Increase Number",
icon='ADD').increment = 1
layout.operator("file.filenum", text="Decrease Number",
icon='REMOVE').increment = -1
layout.separator()
layout.operator("file.rename", text="Rename")
# layout.operator("file.delete")
layout.operator("file.directory_new", text="New Folder")
layout.operator("file.bookmark_add", text="Add Bookmark")
layout.separator()
layout.prop_menu_enum(params, "display_type")
if params.display_type == 'THUMBNAIL':
layout.prop_menu_enum(params, "display_size")
layout.prop_menu_enum(params, "recursion_level", text="Recursions")
layout.prop_menu_enum(params, "sort_method")
classes = (
FILEBROWSER_HT_header,
FILEBROWSER_PT_display,
FILEBROWSER_PT_filter,
FILEBROWSER_UL_dir,
FILEBROWSER_PT_bookmarks_volumes,
FILEBROWSER_PT_bookmarks_system,
@ -272,7 +522,11 @@ classes = (
FILEBROWSER_PT_bookmarks_favorites,
FILEBROWSER_PT_bookmarks_recents,
FILEBROWSER_PT_advanced_filter,
FILEBROWSER_PT_directory_path,
FILEBROWSER_PT_file_operation,
FILEBROWSER_PT_options_toggle,
FILEBROWSER_MT_view,
FILEBROWSER_MT_context_menu,
)
if __name__ == "__main__": # only for live edit.

View File

@ -99,8 +99,13 @@ void BLI_filelist_entry_size_to_string(const struct stat *st,
void BLI_filelist_entry_mode_to_string(
const struct stat *st, const bool compact, char r_mode1[], char r_mode2[], char r_mode3[]);
void BLI_filelist_entry_owner_to_string(const struct stat *st, const bool compact, char r_owner[]);
void BLI_filelist_entry_datetime_to_string(
const struct stat *st, const int64_t ts, const bool compact, char r_time[], char r_date[]);
void BLI_filelist_entry_datetime_to_string(const struct stat *st,
const int64_t ts,
const bool compact,
char r_time[],
char r_date[],
bool *r_is_today,
bool *r_is_yesterday);
/* Files */

View File

@ -261,36 +261,17 @@ unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_
*/
void BLI_filelist_entry_size_to_string(const struct stat *st,
const uint64_t sz,
const bool compact,
/* Used to change MB -> M, etc. - is that really useful? */
const bool UNUSED(compact),
char r_size[FILELIST_DIRENTRY_SIZE_LEN])
{
double size;
const char *fmt;
const char *units[] = {"KiB", "MiB", "GiB", "TiB", NULL};
const char *units_compact[] = {"K", "M", "G", "T", NULL};
const char *unit = "B";
/*
* Seems st_size is signed 32-bit value in *nix and Windows. This
* will buy us some time until files get bigger than 4GB or until
* everyone starts using __USE_FILE_OFFSET64 or equivalent.
*/
size = (double)(st ? st->st_size : sz);
if (size > 1024.0) {
const char **u;
for (u = compact ? units_compact : units, size /= 1024.0; size > 1024.0 && *(u + 1);
u++, size /= 1024.0) {
/* pass */
}
fmt = size > 100.0 ? "%.0f %s" : (size > 10.0 ? "%.1f %s" : "%.2f %s");
unit = *u;
}
else {
fmt = "%.0f %s";
}
BLI_snprintf(r_size, sizeof(*r_size) * FILELIST_DIRENTRY_SIZE_LEN, fmt, size, unit);
double size = (double)(st ? st->st_size : sz);
BLI_str_format_byte_unit(r_size, size, true);
}
/**
@ -366,14 +347,45 @@ void BLI_filelist_entry_owner_to_string(const struct stat *st,
/**
* Convert given entry's time into human-readable strings.
*
* \param r_is_today: optional, returns true if the date matches today's.
* \param r_is_yesterday: optional, returns true if the date matches yesterday's.
*/
void BLI_filelist_entry_datetime_to_string(const struct stat *st,
const int64_t ts,
const bool compact,
char r_time[FILELIST_DIRENTRY_TIME_LEN],
char r_date[FILELIST_DIRENTRY_DATE_LEN])
char r_date[FILELIST_DIRENTRY_DATE_LEN],
bool *r_is_today,
bool *r_is_yesterday)
{
time_t ts_mtime = ts;
int today_year = 0;
int today_yday = 0;
int yesterday_year = 0;
int yesterday_yday = 0;
if (r_is_today || r_is_yesterday) {
/* Localtime() has only one buffer so need to get data out before called again. */
const time_t ts_now = time(NULL);
struct tm *today = localtime(&ts_now);
today_year = today->tm_year;
today_yday = today->tm_yday;
/* Handle a yesterday that spans a year */
today->tm_mday--;
mktime(today);
yesterday_year = today->tm_year;
yesterday_yday = today->tm_yday;
if (r_is_today) {
*r_is_today = false;
}
if (r_is_yesterday) {
*r_is_yesterday = false;
}
}
const time_t ts_mtime = ts;
const struct tm *tm = localtime(st ? &st->st_mtime : &ts_mtime);
const time_t zero = 0;
@ -385,12 +397,20 @@ void BLI_filelist_entry_datetime_to_string(const struct stat *st,
if (r_time) {
strftime(r_time, sizeof(*r_time) * FILELIST_DIRENTRY_TIME_LEN, "%H:%M", tm);
}
if (r_date) {
strftime(r_date,
sizeof(*r_date) * FILELIST_DIRENTRY_DATE_LEN,
compact ? "%d/%m/%y" : "%d-%b-%y",
compact ? "%d/%m/%y" : "%d %b %Y",
tm);
}
if (r_is_today && (tm->tm_year == today_year) && (tm->tm_yday == today_yday)) {
*r_is_today = true;
}
else if (r_is_yesterday && (tm->tm_year == yesterday_year) && (tm->tm_yday == yesterday_yday)) {
*r_is_yesterday = true;
}
}
/**

View File

@ -3722,5 +3722,39 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* Versioning code until next subversion bump goes here. */
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
ARegion *ar_ui = do_versions_find_region(regionbase, RGN_TYPE_UI);
ARegion *ar_header = do_versions_find_region(regionbase, RGN_TYPE_HEADER);
ARegion *ar_toolprops = do_versions_find_region_or_null(regionbase,
RGN_TYPE_TOOL_PROPS);
/* Reinsert UI region so that it spawns entire area width */
BLI_remlink(regionbase, ar_ui);
BLI_insertlinkafter(regionbase, ar_header, ar_ui);
ar_ui->flag |= RGN_FLAG_DYNAMIC_SIZE;
if (ar_toolprops && (ar_toolprops->alignment == (RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV))) {
SpaceType *stype = BKE_spacetype_from_id(sl->spacetype);
/* Remove empty region at old location. */
BLI_assert(sfile->op == NULL);
BKE_area_region_free(stype, ar_toolprops);
BLI_freelinkN(regionbase, ar_toolprops);
}
if (sfile->params) {
sfile->params->details_flags |= FILE_DETAILS_SIZE | FILE_DETAILS_DATETIME;
}
}
}
}
}
}
}

View File

@ -150,6 +150,7 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
* Include next version bump.
*/
{
FROM_DEFAULT_V4_UCHAR(space_file.execution_buts);
}
#undef FROM_DEFAULT_V4_UCHAR

View File

@ -34,17 +34,32 @@ struct wmWindowManager;
#define FILE_LAYOUT_HOR 1
#define FILE_LAYOUT_VER 2
#define MAX_FILE_COLUMN 4
typedef enum FileListColumns {
typedef enum FileAttributeColumnType {
COLUMN_NONE = -1,
COLUMN_NAME = 0,
COLUMN_DATE,
COLUMN_TIME,
COLUMN_DATETIME,
COLUMN_SIZE,
} FileListColumns;
ATTRIBUTE_COLUMN_MAX
} FileAttributeColumnType;
typedef struct FileAttributeColumn {
/** UI name for this column */
const char *name;
float width;
/* The sort type to use when sorting by this column. */
int sort_type; /* eFileSortType */
/* Alignment of column texts, header text is always left aligned */
int text_align; /* eFontStyle_Align */
} FileAttributeColumn;
typedef struct FileLayout {
/* view settings - XXX - move into own struct */
int offset_top;
/* Height of the header for the different FileAttributeColumn's. */
int attribute_column_header_h;
int prv_w;
int prv_h;
int tile_w;
@ -54,13 +69,17 @@ typedef struct FileLayout {
int prv_border_x;
int prv_border_y;
int rows;
int columns;
/* Those are the major layout columns the files are distributed across, not to be confused with
* 'attribute_columns' array below. */
int flow_columns;
int width;
int height;
int flag;
int dirty;
int textheight;
float column_widths[MAX_FILE_COLUMN];
/* The columns for each item (name, modification date/time, size). Not to be confused with the
* 'flow_columns' above. */
FileAttributeColumn attribute_columns[ATTRIBUTE_COLUMN_MAX];
/* When we change display size, we may have to update static strings like size of files... */
short curr_size;
@ -72,6 +91,7 @@ typedef struct FileSelection {
} FileSelection;
struct rcti;
struct View2D;
struct FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile);
@ -87,6 +107,17 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, struct ARegion *ar);
int ED_fileselect_layout_offset(FileLayout *layout, int x, int y);
FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const struct rcti *rect);
void ED_fileselect_layout_maskrect(const FileLayout *layout,
const struct View2D *v2d,
struct rcti *r_rect);
bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout,
const struct View2D *v2d,
int x,
int y);
bool ED_fileselect_layout_isect_rect(const FileLayout *layout,
const struct View2D *v2d,
const struct rcti *rect,
struct rcti *r_dst);
void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y);
void ED_operatormacros_file(void);

View File

@ -2244,6 +2244,8 @@ int UI_idcode_icon_get(const int idcode)
return ICON_FONT_DATA;
case ID_WO:
return ICON_WORLD_DATA;
case ID_WS:
return ICON_WORKSPACE;
default:
return ICON_NONE;
}

View File

@ -1265,9 +1265,6 @@ static void region_rect_recursive(
else if (ED_area_is_global(sa)) {
prefsizey = ED_region_global_size_y();
}
else if (ar->regiontype == RGN_TYPE_UI && sa->spacetype == SPACE_FILE) {
prefsizey = UI_UNIT_Y * 2 + (UI_UNIT_Y / 2);
}
else {
prefsizey = UI_DPI_FAC * (ar->sizey > 1 ? ar->sizey + 0.5f : ar->type->prefsizey);
}

View File

@ -3935,6 +3935,65 @@ static void SCREEN_OT_region_quadview(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Region Toggle Operator
* \{ */
static int region_toggle_exec(bContext *C, wmOperator *op)
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "region_type");
ARegion *region;
if (RNA_property_is_set(op->ptr, prop)) {
region = BKE_area_find_region_type(CTX_wm_area(C), RNA_property_enum_get(op->ptr, prop));
}
else {
region = CTX_wm_region(C);
}
if (region) {
ED_region_toggle_hidden(C, region);
}
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
}
static bool region_toggle_poll(bContext *C)
{
ScrArea *area = CTX_wm_area(C);
/* don't flip anything around in topbar */
if (area && area->spacetype == SPACE_TOPBAR) {
CTX_wm_operator_poll_msg_set(C, "Toggling regions in the Top-bar is not allowed");
return 0;
}
return ED_operator_areaactive(C);
}
static void SCREEN_OT_region_toggle(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Toggle Region";
ot->idname = "SCREEN_OT_region_toggle";
ot->description = "Hide or unhide the region";
/* api callbacks */
ot->exec = region_toggle_exec;
ot->poll = region_toggle_poll;
ot->flag = 0;
RNA_def_enum(ot->srna,
"region_type",
rna_enum_region_type_items,
0,
"Region Type",
"Type of the region to toggle");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Region Flip Operator
* \{ */
@ -5361,6 +5420,7 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(SCREEN_OT_area_swap);
WM_operatortype_append(SCREEN_OT_region_quadview);
WM_operatortype_append(SCREEN_OT_region_scale);
WM_operatortype_append(SCREEN_OT_region_toggle);
WM_operatortype_append(SCREEN_OT_region_flip);
WM_operatortype_append(SCREEN_OT_header_toggle_menus);
WM_operatortype_append(SCREEN_OT_region_context_menu);

View File

@ -710,7 +710,7 @@ static void SOUND_OT_mixdown(wmOperatorType *ot)
FILE_TYPE_FOLDER | FILE_TYPE_SOUND,
FILE_SPECIAL,
FILE_SAVE,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
#ifdef WITH_AUDASPACE

View File

@ -26,6 +26,7 @@
#include <errno.h>
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
#include "BLI_utildefines.h"
#include "BLI_math.h"
@ -77,234 +78,6 @@ static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char
return BLI_strdup(dyn_tooltip);
}
/* Note: This function uses pixelspace (0, 0, winx, winy), not view2d.
* The controls are laid out as follows:
*
* -------------------------------------------
* | Directory input | execute |
* -------------------------------------------
* | Filename input | + | - | cancel |
* -------------------------------------------
*
* The input widgets will stretch to fill any excess space.
* When there isn't enough space for all controls to be shown, they are
* hidden in this order: x/-, execute/cancel, input widgets.
*/
void file_draw_buttons(const bContext *C, ARegion *ar)
{
/* Button layout. */
const int max_x = ar->winx - 10;
const int line1_y = ar->winy - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN);
const int line2_y = line1_y - (IMASEL_BUTTONS_HEIGHT / 2 + IMASEL_BUTTONS_MARGIN);
const int input_minw = 20;
const int btn_h = UI_UNIT_Y;
const int btn_fn_w = UI_UNIT_X;
const int btn_minw = 80;
const int btn_margin = 20;
const int separator = 4;
/* Additional locals. */
char uiblockstr[32];
int loadbutton;
int fnumbuttons;
int min_x = 10;
int chan_offs = 0;
int available_w = max_x - min_x;
int line1_w = available_w;
int line2_w = available_w;
uiBut *but;
uiBlock *block;
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_params(sfile);
ARegion *artmp;
const bool is_browse_only = (sfile->op == NULL);
/* Initialize UI block. */
BLI_snprintf(uiblockstr, sizeof(uiblockstr), "win %p", (void *)ar);
block = UI_block_begin(C, ar, uiblockstr, UI_EMBOSS);
/* exception to make space for collapsed region icon */
for (artmp = CTX_wm_area(C)->regionbase.first; artmp; artmp = artmp->next) {
if (artmp->regiontype == RGN_TYPE_TOOLS && artmp->flag & RGN_FLAG_HIDDEN) {
chan_offs = 16;
min_x += chan_offs;
available_w -= chan_offs;
}
}
/* Is there enough space for the execute / cancel buttons? */
if (is_browse_only) {
loadbutton = 0;
}
else {
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
loadbutton = UI_fontstyle_string_width(fstyle, params->title) + btn_margin;
CLAMP_MIN(loadbutton, btn_minw);
if (available_w <= loadbutton + separator + input_minw) {
loadbutton = 0;
}
}
if (loadbutton) {
line1_w -= (loadbutton + separator);
line2_w = line1_w;
}
/* Is there enough space for file number increment/decrement buttons? */
fnumbuttons = 2 * btn_fn_w;
if (!loadbutton || line2_w <= fnumbuttons + separator + input_minw) {
fnumbuttons = 0;
}
else {
line2_w -= (fnumbuttons + separator);
}
/* Text input fields for directory and file. */
if (available_w > 0) {
const struct FileDirEntry *file = sfile->files ?
filelist_file(sfile->files, params->active_file) :
NULL;
int overwrite_alert = file_draw_check_exists(sfile);
const bool is_active_dir = file && (file->typeflag & FILE_TYPE_FOLDER);
/* callbacks for operator check functions */
UI_block_func_set(block, file_draw_check_cb, NULL, NULL);
but = uiDefBut(block,
UI_BTYPE_TEXT,
-1,
"",
min_x,
line1_y,
line1_w - chan_offs,
btn_h,
params->dir,
0.0,
(float)FILE_MAX,
0,
0,
TIP_("File path"));
UI_but_func_complete_set(but, autocomplete_directory, NULL);
UI_but_flag_enable(but, UI_BUT_NO_UTF8);
UI_but_flag_disable(but, UI_BUT_UNDO);
UI_but_funcN_set(but, file_directory_enter_handle, NULL, but);
/* TODO, directory editing is non-functional while a library is loaded
* until this is properly supported just disable it. */
if (sfile->files && filelist_lib(sfile->files)) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
if ((params->flag & FILE_DIRSEL_ONLY) == 0) {
but = uiDefBut(
block,
UI_BTYPE_TEXT,
-1,
"",
min_x,
line2_y,
line2_w - chan_offs,
btn_h,
is_active_dir ? (char *)"" : params->file,
0.0,
(float)FILE_MAXFILE,
0,
0,
TIP_(overwrite_alert ? N_("File name, overwrite existing") : N_("File name")));
UI_but_func_complete_set(but, autocomplete_file, NULL);
UI_but_flag_enable(but, UI_BUT_NO_UTF8);
UI_but_flag_disable(but, UI_BUT_UNDO);
/* silly workaround calling NFunc to ensure this does not get called
* immediate ui_apply_but_func but only after button deactivates */
UI_but_funcN_set(but, file_filename_enter_handle, NULL, but);
/* check if this overrides a file and if the operator option is used */
if (overwrite_alert) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
}
/* clear func */
UI_block_func_set(block, NULL, NULL, NULL);
}
/* Filename number increment / decrement buttons. */
if (fnumbuttons && (params->flag & FILE_DIRSEL_ONLY) == 0) {
UI_block_align_begin(block);
but = uiDefIconButO(block,
UI_BTYPE_BUT,
"FILE_OT_filenum",
0,
ICON_REMOVE,
min_x + line2_w + separator - chan_offs,
line2_y,
btn_fn_w,
btn_h,
TIP_("Decrement the filename number"));
RNA_int_set(UI_but_operator_ptr_get(but), "increment", -1);
but = uiDefIconButO(block,
UI_BTYPE_BUT,
"FILE_OT_filenum",
0,
ICON_ADD,
min_x + line2_w + separator + btn_fn_w - chan_offs,
line2_y,
btn_fn_w,
btn_h,
TIP_("Increment the filename number"));
RNA_int_set(UI_but_operator_ptr_get(but), "increment", 1);
UI_block_align_end(block);
}
/* Execute / cancel buttons. */
if (loadbutton) {
const struct FileDirEntry *file = sfile->files ?
filelist_file(sfile->files, params->active_file) :
NULL;
char const *str_exec;
if (file && FILENAME_IS_PARENT(file->relpath)) {
str_exec = IFACE_("Parent Directory");
}
else if (file && file->typeflag & FILE_TYPE_DIR) {
str_exec = IFACE_("Open Directory");
}
else {
str_exec = params->title; /* params->title is already translated! */
}
but = uiDefButO(block,
UI_BTYPE_BUT,
"FILE_OT_execute",
WM_OP_EXEC_REGION_WIN,
str_exec,
max_x - loadbutton,
line1_y,
loadbutton,
btn_h,
"");
/* Just a display hint. */
UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT);
uiDefButO(block,
UI_BTYPE_BUT,
"FILE_OT_cancel",
WM_OP_EXEC_REGION_WIN,
IFACE_("Cancel"),
max_x - loadbutton,
line2_y,
loadbutton,
btn_h,
"");
}
UI_block_end(C, block);
UI_block_draw(C, block);
}
static void draw_tile(int sx, int sy, int width, int height, int colorid, int shade)
{
float color[4];
@ -349,7 +122,7 @@ static void file_draw_string(int sx,
rcti rect;
char fname[FILE_MAXFILE];
if (string[0] == '\0') {
if (string[0] == '\0' || width < 1) {
return;
}
@ -362,7 +135,7 @@ static void file_draw_string(int sx,
/* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict
* (for buttons it works) */
rect.xmin = sx;
rect.xmax = (int)(sx + ceil(width + 5.0f / UI_DPI_FAC));
rect.xmax = sx + round_fl_to_int(width);
rect.ymin = sy - height;
rect.ymax = sy;
@ -404,8 +177,8 @@ static void file_draw_preview(uiBlock *block,
float scaledx, scaledy;
float scale;
int ex, ey;
bool use_dropshadow = !is_icon && (typeflags & FILE_TYPE_IMAGE);
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
bool use_dropshadow = !is_icon &&
(typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
BLI_assert(imb != NULL);
@ -442,13 +215,27 @@ static void file_draw_preview(uiBlock *block,
/* shadow */
if (use_dropshadow) {
UI_draw_box_shadow(220, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
UI_draw_box_shadow(128, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
}
GPU_blend(true);
/* the image */
if (!is_icon && typeflags & FILE_TYPE_FTFONT) {
/* the large image */
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (is_icon) {
/* File and Folder icons draw with lowered opacity until we add themes */
col[3] = 0.6f;
/* Use dark images if background is light */
float bg[3];
UI_GetThemeColor3fv(TH_BACK, bg);
if (rgb_to_grayscale(bg) > 0.5f) {
col[0] = 0;
col[1] = 0;
col[2] = 0;
}
}
else if (typeflags & FILE_TYPE_FTFONT) {
UI_GetThemeColor4fv(TH_TEXT, col);
}
@ -477,30 +264,61 @@ static void file_draw_preview(uiBlock *block,
GPU_blend_set_func_separate(
GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA);
if (icon) {
UI_icon_draw_ex((float)xco + (7 * UI_DPI_FAC),
(float)yco + (7 * UI_DPI_FAC),
icon,
icon_aspect,
1.0f,
0.0f,
NULL,
false);
if (icon && (icon != ICON_FILE_FONT)) {
/* size of center icon is scaled to fit container and UI scale */
float icon_x, icon_y;
if (is_icon) {
const float icon_size = 16.0f / icon_aspect * U.dpi_fac;
float icon_opacity = MIN2(icon_aspect, 0.7);
uchar icon_color[4] = {255, 255, 255, 255};
float bg[3];
/* base this off theme color of file or folder later */
UI_GetThemeColor3fv(TH_BACK, bg);
if (rgb_to_grayscale(bg) > 0.5f) {
icon_color[0] = 0;
icon_color[1] = 0;
icon_color[2] = 0;
}
icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f);
icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.65f));
UI_icon_draw_ex(
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
}
else {
const uchar dark[4] = {0, 0, 0, 255};
const uchar light[4] = {255, 255, 255, 255};
/* Smaller, fainter icon for preview image thumbnail. */
icon_x = xco + (2.0f * UI_DPI_FAC);
icon_y = yco + (2.0f * UI_DPI_FAC);
UI_icon_draw_ex(icon_x + 1, icon_y - 1, icon, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false);
UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false);
}
}
/* border */
if (use_dropshadow) {
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint pos_attr = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint col_attr = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformColor4f(0.0f, 0.0f, 0.0f, 0.4f);
imm_draw_box_wire_2d(pos, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
immBegin(GPU_PRIM_LINE_LOOP, 4);
immAttr4f(col_attr, 1.0f, 1.0f, 1.0f, 0.15f);
immVertex2f(pos_attr, (float)xco + 1, (float)(yco + ey));
immAttr4f(col_attr, 1.0f, 1.0f, 1.0f, 0.2f);
immVertex2f(pos_attr, (float)(xco + ex), (float)(yco + ey));
immAttr4f(col_attr, 0.0f, 0.0f, 0.0f, 0.2f);
immVertex2f(pos_attr, (float)(xco + ex), (float)yco + 1);
immAttr4f(col_attr, 0.0f, 0.0f, 0.0f, 0.3f);
immVertex2f(pos_attr, (float)xco + 1, (float)yco + 1);
immEnd();
immUnbindProgram();
}
but = uiDefBut(block, UI_BTYPE_LABEL, 0, "", xco, yco, ex, ey, NULL, 0.0, 0.0, 0, 0, NULL);
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
/* dragregion */
if (drag) {
@ -557,6 +375,7 @@ static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
static void draw_background(FileLayout *layout, View2D *v2d)
{
const int item_height = layout->tile_h + (2 * layout->tile_border_y);
int i;
int sy;
@ -565,9 +384,11 @@ static void draw_background(FileLayout *layout, View2D *v2d)
immUniformThemeColorShade(TH_BACK, -7);
/* alternating flat shade background */
for (i = 0; (i <= layout->rows); i += 2) {
sy = (int)v2d->cur.ymax - i * (layout->tile_h + 2 * layout->tile_border_y) -
layout->tile_border_y;
for (i = 2; (i <= layout->rows + 1); i += 2) {
sy = (int)v2d->cur.ymax - layout->offset_top - i * item_height - layout->tile_border_y;
/* Offsett pattern slightly to add scroll effect. */
sy += round_fl_to_int(item_height * (v2d->tot.ymax - v2d->cur.ymax) / item_height);
immRectf(pos,
v2d->cur.xmin,
@ -632,6 +453,176 @@ static void draw_dividers(FileLayout *layout, View2D *v2d)
}
}
static void draw_columnheader_background(const FileLayout *layout, const View2D *v2d)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, 11);
immRectf(pos,
v2d->cur.xmin,
v2d->cur.ymax - layout->attribute_column_header_h,
v2d->cur.xmax,
v2d->cur.ymax);
immUnbindProgram();
}
static void draw_columnheader_columns(const FileSelectParams *params,
FileLayout *layout,
const View2D *v2d,
const uchar text_col[4])
{
const float divider_pad = 0.2 * layout->attribute_column_header_h;
int sx = v2d->cur.xmin, sy = v2d->cur.ymax;
for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX;
column_type++) {
if (!file_attribute_column_type_enabled(params, column_type)) {
continue;
}
const FileAttributeColumn *column = &layout->attribute_columns[column_type];
/* Active sort type triangle */
if (params->sort == column->sort_type) {
float tri_color[4];
rgba_uchar_to_float(tri_color, text_col);
UI_draw_icon_tri(sx + column->width - (0.3f * U.widget_unit) -
ATTRIBUTE_COLUMN_PADDING / 2.0f,
sy + (0.1f * U.widget_unit) - (layout->attribute_column_header_h / 2),
(params->flag & FILE_SORT_INVERT) ? 't' : 'v',
tri_color);
}
file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING,
sy - layout->tile_border_y,
IFACE_(column->name),
column->width - 2 * ATTRIBUTE_COLUMN_PADDING,
layout->attribute_column_header_h - layout->tile_border_y,
UI_STYLE_TEXT_LEFT,
text_col);
/* Separator line */
if (column_type != COLUMN_NAME) {
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, -10);
immBegin(GPU_PRIM_LINES, 2);
immVertex2f(pos, sx - 1, sy - divider_pad);
immVertex2f(pos, sx - 1, sy - layout->attribute_column_header_h + divider_pad);
immEnd();
immUnbindProgram();
}
sx += column->width;
}
/* Vertical separator lines line */
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, -10);
immBegin(GPU_PRIM_LINES, 4);
immVertex2f(pos, v2d->cur.xmin, sy);
immVertex2f(pos, v2d->cur.xmax, sy);
immVertex2f(pos, v2d->cur.xmin, sy - layout->attribute_column_header_h);
immVertex2f(pos, v2d->cur.xmax, sy - layout->attribute_column_header_h);
immEnd();
immUnbindProgram();
}
}
/**
* Updates the stat string stored in file->entry if necessary.
*/
static const char *filelist_get_details_column_string(FileAttributeColumnType column,
const FileDirEntry *file,
const bool small_size,
const bool update_stat_strings)
{
switch (column) {
case COLUMN_DATETIME:
if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) {
if ((file->entry->datetime_str[0] == '\0') || update_stat_strings) {
char date[FILELIST_DIRENTRY_DATE_LEN], time[FILELIST_DIRENTRY_TIME_LEN];
bool is_today, is_yesterday;
BLI_filelist_entry_datetime_to_string(
NULL, file->entry->time, small_size, time, date, &is_today, &is_yesterday);
if (is_today || is_yesterday) {
BLI_strncpy(date, is_today ? N_("Today") : N_("Yesterday"), sizeof(date));
}
BLI_snprintf(
file->entry->datetime_str, sizeof(file->entry->datetime_str), "%s %s", date, time);
}
return file->entry->datetime_str;
}
break;
case COLUMN_SIZE:
if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
!(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_size_to_string(
NULL, file->entry->size, small_size, file->entry->size_str);
}
return file->entry->size_str;
}
break;
default:
break;
}
return NULL;
}
static void draw_details_columns(const FileSelectParams *params,
const FileLayout *layout,
const FileDirEntry *file,
const int pos_x,
const int pos_y,
const uchar text_col[4])
{
const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size);
int sx = pos_x - layout->tile_border_x - (UI_UNIT_X * 0.1f), sy = pos_y;
for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX;
column_type++) {
const FileAttributeColumn *column = &layout->attribute_columns[column_type];
/* Name column is not a detail column (should already be drawn), always skip here. */
if (column_type == COLUMN_NAME) {
sx += column->width;
continue;
}
if (!file_attribute_column_type_enabled(params, column_type)) {
continue;
}
const char *str = filelist_get_details_column_string(
column_type, file, small_size, update_stat_strings);
if (str) {
file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING,
sy - layout->tile_border_y,
IFACE_(str),
column->width - 2 * ATTRIBUTE_COLUMN_PADDING,
layout->tile_h,
column->text_align,
text_col);
}
sx += column->width;
}
}
void file_draw_list(const bContext *C, ARegion *ar)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@ -652,18 +643,14 @@ void file_draw_list(const bContext *C, ARegion *ar)
bool is_icon;
eFontStyle_Align align;
bool do_drag;
int column_space = 0.6f * UI_UNIT_X;
unsigned char text_col[4];
const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size);
const float thumb_icon_aspect = sqrtf(64.0f / (float)(params->thumbnail_size));
const bool draw_columnheader = (params->display == FILE_VERTICALDISPLAY);
const float thumb_icon_aspect = MIN2(64.0f / (float)(params->thumbnail_size), 1.0f);
numfiles = filelist_files_ensure(files);
if (params->display != FILE_IMGDISPLAY) {
draw_background(layout, v2d);
draw_dividers(layout, v2d);
}
@ -679,13 +666,14 @@ void file_draw_list(const bContext *C, ARegion *ar)
numfiles_layout += layout->rows;
}
else {
numfiles_layout += layout->columns;
numfiles_layout += layout->flow_columns;
}
filelist_file_cache_slidingwindow_set(files, numfiles_layout);
textwidth = (FILE_IMGDISPLAY == params->display) ? layout->tile_w :
(int)layout->column_widths[COLUMN_NAME];
textwidth = (FILE_IMGDISPLAY == params->display) ?
layout->tile_w :
round_fl_to_int(layout->attribute_columns[COLUMN_NAME].width);
textheight = (int)(layout->textheight * 3.0 / 2.0 + 0.5);
align = (FILE_IMGDISPLAY == params->display) ? UI_STYLE_TEXT_CENTER : UI_STYLE_TEXT_LEFT;
@ -719,11 +707,16 @@ void file_draw_list(const bContext *C, ARegion *ar)
BLF_batch_draw_begin();
UI_GetThemeColor4ubv(TH_TEXT, text_col);
for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) {
unsigned int file_selflag;
char path[FILE_MAX_LIBEXTRA];
int padx = 0.1f * UI_UNIT_X;
int icon_ofs = 0;
ED_fileselect_layout_tilepos(layout, i, &sx, &sy);
sx += (int)(v2d->tot.xmin + 0.1f * UI_UNIT_X);
sx += (int)(v2d->tot.xmin + padx);
sy = (int)(v2d->tot.ymax - sy);
file = filelist_file(files, i);
@ -737,15 +730,14 @@ void file_draw_list(const bContext *C, ARegion *ar)
int colorid = (file_selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
int shade = (params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ? 35 :
0;
const short width = ELEM(params->display, FILE_VERTICALDISPLAY, FILE_HORIZONTALDISPLAY) ?
layout->tile_w - (2 * padx) :
layout->tile_w;
BLI_assert(i == 0 || !FILENAME_IS_CURRPAR(file->relpath));
draw_tile(sx,
sy - 1,
layout->tile_w + 4,
sfile->layout->tile_h + layout->tile_border_y,
colorid,
shade);
draw_tile(
sx, sy - 1, width, sfile->layout->tile_h + layout->tile_border_y, colorid, shade);
}
}
UI_draw_roundbox_corner_set(UI_CNR_NONE);
@ -778,38 +770,28 @@ void file_draw_list(const bContext *C, ARegion *ar)
file_draw_icon(block,
path,
sx,
sy - (UI_UNIT_Y / 6),
sy - layout->tile_border_y,
filelist_geticon(files, i, true),
ICON_DEFAULT_WIDTH_SCALE,
ICON_DEFAULT_HEIGHT_SCALE,
do_drag);
sx += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
}
UI_GetThemeColor4ubv(TH_TEXT, text_col);
if (file_selflag & FILE_SEL_EDITING) {
uiBut *but;
short width;
if (params->display == FILE_SHORTDISPLAY) {
width = layout->tile_w - (ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X);
}
else if (params->display == FILE_LONGDISPLAY) {
width = layout->column_widths[COLUMN_NAME] + (column_space * 3.5f);
}
else {
BLI_assert(params->display == FILE_IMGDISPLAY);
width = textwidth;
}
const short width = (params->display == FILE_IMGDISPLAY) ?
textwidth :
layout->attribute_columns[COLUMN_NAME].width -
ATTRIBUTE_COLUMN_PADDING;
but = uiDefBut(block,
UI_BTYPE_TEXT,
1,
"",
sx,
sx + icon_ofs,
sy - layout->tile_h - 0.15f * UI_UNIT_X,
width,
width - icon_ofs,
textheight,
sfile->params->renamefile,
1.0f,
@ -825,74 +807,19 @@ void file_draw_list(const bContext *C, ARegion *ar)
sfile->files, file, FILE_SEL_REMOVE, FILE_SEL_EDITING, CHECK_ALL);
}
}
if (!(file_selflag & FILE_SEL_EDITING)) {
int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight :
sy;
file_draw_string(sx + 1, tpos, file->name, (float)textwidth, textheight, align, text_col);
else {
const int txpos = (params->display == FILE_IMGDISPLAY) ? sx : sx + 1 + icon_ofs;
const int typos = (params->display == FILE_IMGDISPLAY) ?
sy - layout->tile_h + layout->textheight :
sy - layout->tile_border_y;
const int twidth = (params->display == FILE_IMGDISPLAY) ?
textwidth :
textwidth - 1 - icon_ofs - padx - layout->tile_border_x;
file_draw_string(txpos, typos, file->name, (float)twidth, textheight, align, text_col);
}
sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
if (params->display == FILE_SHORTDISPLAY) {
if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
!(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_size_to_string(
NULL, file->entry->size, small_size, file->entry->size_str);
}
file_draw_string(sx,
sy,
file->entry->size_str,
layout->column_widths[COLUMN_SIZE],
layout->tile_h,
align,
text_col);
}
sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
}
else if (params->display == FILE_LONGDISPLAY) {
if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) {
if ((file->entry->date_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_datetime_to_string(
NULL, file->entry->time, small_size, file->entry->time_str, file->entry->date_str);
}
file_draw_string(sx,
sy,
file->entry->date_str,
layout->column_widths[COLUMN_DATE],
layout->tile_h,
align,
text_col);
sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
file_draw_string(sx,
sy,
file->entry->time_str,
layout->column_widths[COLUMN_TIME],
layout->tile_h,
align,
text_col);
sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
}
else {
sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
}
if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
!(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
if ((file->entry->size_str[0] == '\0') || update_stat_strings) {
BLI_filelist_entry_size_to_string(
NULL, file->entry->size, small_size, file->entry->size_str);
}
file_draw_string(sx,
sy,
file->entry->size_str,
layout->column_widths[COLUMN_SIZE],
layout->tile_h,
align,
text_col);
}
sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
if (params->display != FILE_IMGDISPLAY) {
draw_details_columns(params, layout, file, sx, sy, text_col);
}
}
@ -901,5 +828,11 @@ void file_draw_list(const bContext *C, ARegion *ar)
UI_block_end(C, block);
UI_block_draw(C, block);
/* Draw last, on top of file list. */
if (draw_columnheader) {
draw_columnheader_background(layout, v2d);
draw_columnheader_columns(params, layout, v2d, text_col);
}
layout->curr_size = params->thumbnail_size;
}

View File

@ -30,9 +30,11 @@ struct ARegion;
struct ARegionType;
struct FileSelectParams;
struct SpaceFile;
struct View2D;
/* file_ops.c */
struct ARegion *file_tools_region(struct ScrArea *sa);
struct ARegion *file_tool_props_region(struct ScrArea *sa);
/* file_draw.c */
#define TILE_BORDER_X (UI_UNIT_X / 4)
@ -42,9 +44,10 @@ struct ARegion *file_tools_region(struct ScrArea *sa);
#define IMASEL_BUTTONS_HEIGHT (UI_UNIT_Y * 2)
#define IMASEL_BUTTONS_MARGIN (UI_UNIT_Y / 6)
#define ATTRIBUTE_COLUMN_PADDING (0.5f * UI_UNIT_X)
#define SMALL_SIZE_CHECK(_size) ((_size) < 64) /* Related to FileSelectParams.thumbnail_size. */
void file_draw_buttons(const bContext *C, ARegion *ar);
void file_calc_previews(const bContext *C, ARegion *ar);
void file_draw_list(const bContext *C, ARegion *ar);
@ -64,6 +67,7 @@ typedef enum WalkSelectDirection {
} WalkSelectDirections;
void FILE_OT_highlight(struct wmOperatorType *ot);
void FILE_OT_sort_column_ui_context(struct wmOperatorType *ot);
void FILE_OT_select(struct wmOperatorType *ot);
void FILE_OT_select_walk(struct wmOperatorType *ot);
void FILE_OT_select_all(struct wmOperatorType *ot);
@ -112,6 +116,16 @@ void file_operator_to_sfile(bContext *C, struct SpaceFile *sfile, struct wmOpera
/* filesel.c */
void fileselect_file_set(SpaceFile *sfile, const int index);
bool file_attribute_column_type_enabled(const FileSelectParams *params,
FileAttributeColumnType column);
bool file_attribute_column_header_is_inside(const struct View2D *v2d,
const FileLayout *layout,
int x,
int y);
FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d,
const FileSelectParams *params,
FileLayout *layout,
int x);
float file_string_width(const char *str);
float file_font_pointsize(void);

View File

@ -78,7 +78,12 @@ static FileSelection find_file_mouse_rect(SpaceFile *sfile, ARegion *ar, const r
BLI_rctf_rcti_copy(&rect_region_fl, rect_region);
/* Okay, manipulating v2d rects here is hacky... */
v2d->mask.ymax -= sfile->layout->offset_top;
v2d->cur.ymax -= sfile->layout->offset_top;
UI_view2d_region_to_view_rctf(v2d, &rect_region_fl, &rect_view_fl);
v2d->mask.ymax += sfile->layout->offset_top;
v2d->cur.ymax += sfile->layout->offset_top;
BLI_rcti_init(&rect_view,
(int)(v2d->tot.xmin + rect_view_fl.xmin),
@ -190,7 +195,6 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
const bool is_parent_dir = FILENAME_IS_PARENT(file->relpath);
if (do_diropen == false) {
params->file[0] = '\0';
retval = FILE_SELECT_DIR;
}
/* the path is too long and we are not going up! */
@ -262,8 +266,8 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i
cur->ymax = cur->ymin + ar->winy;
}
/* up */
else if (cur->ymax < rect.ymax) {
cur->ymax = rect.ymax + layout->tile_border_y;
else if ((cur->ymax - layout->offset_top) < rect.ymax) {
cur->ymax = rect.ymax + layout->tile_border_y + layout->offset_top;
cur->ymin = cur->ymax - ar->winy;
}
/* left - also use if tile is wider than viewbounds so view is aligned to file name */
@ -278,7 +282,7 @@ static void file_ensure_inside_viewbounds(ARegion *ar, SpaceFile *sfile, const i
}
else {
BLI_assert(cur->xmin <= rect.xmin && cur->xmax >= rect.xmax && cur->ymin <= rect.ymin &&
cur->ymax >= rect.ymax);
(cur->ymax - layout->offset_top) >= rect.ymax);
changed = false;
}
@ -384,7 +388,7 @@ static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *eve
if (result == OPERATOR_RUNNING_MODAL) {
WM_operator_properties_border_to_rcti(op, &rect);
BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect);
sel = file_selection_get(C, &rect, 0);
if ((sel.first != params->sel_first) || (sel.last != params->sel_last)) {
@ -440,7 +444,7 @@ static int file_box_select_exec(bContext *C, wmOperator *op)
file_deselect_all(sfile, FILE_SEL_SELECTED);
}
BLI_rcti_isect(&(ar->v2d.mask), &rect, &rect);
ED_fileselect_layout_isect_rect(sfile->layout, &ar->v2d, &rect, &rect);
ret = file_select(C, &rect, select ? FILE_SEL_ADD : FILE_SEL_REMOVE, false, false);
@ -493,7 +497,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
rect.xmin = rect.xmax = event->mval[0];
rect.ymin = rect.ymax = event->mval[1];
if (!BLI_rcti_isect_pt(&ar->v2d.mask, rect.xmin, rect.ymin)) {
if (!ED_fileselect_layout_is_inside_pt(sfile->layout, &ar->v2d, rect.xmin, rect.ymin)) {
return OPERATOR_CANCELLED;
}
@ -691,7 +695,7 @@ static bool file_walk_select_do(bContext *C,
if (has_selection) {
ARegion *ar = CTX_wm_region(C);
FileLayout *layout = ED_fileselect_get_layout(sfile, ar);
const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->columns;
const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->flow_columns;
if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_UP) ||
(layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_LEFT)) {
@ -1185,7 +1189,7 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my)
mx -= ar->winrct.xmin;
my -= ar->winrct.ymin;
if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) {
if (ED_fileselect_layout_is_inside_pt(sfile->layout, v2d, mx, my)) {
float fx, fy;
int highlight_file;
@ -1234,6 +1238,53 @@ void FILE_OT_highlight(struct wmOperatorType *ot)
ot->poll = ED_operator_file_active;
}
static int file_column_sort_ui_context_invoke(bContext *C,
wmOperator *UNUSED(op),
const wmEvent *event)
{
const ARegion *ar = CTX_wm_region(C);
SpaceFile *sfile = CTX_wm_space_file(C);
if (file_attribute_column_header_is_inside(
&ar->v2d, sfile->layout, event->mval[0], event->mval[1])) {
const FileAttributeColumnType column_type = file_attribute_column_type_find_isect(
&ar->v2d, sfile->params, sfile->layout, event->mval[0]);
if (column_type != COLUMN_NONE) {
const FileAttributeColumn *column = &sfile->layout->attribute_columns[column_type];
if (column->sort_type != FILE_SORT_NONE) {
if (sfile->params->sort == column->sort_type) {
/* Already sorting by selected column -> toggle sort invert (three state logic). */
sfile->params->flag ^= FILE_SORT_INVERT;
}
else {
sfile->params->sort = column->sort_type;
sfile->params->flag &= ~FILE_SORT_INVERT;
}
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
}
}
}
return OPERATOR_PASS_THROUGH;
}
void FILE_OT_sort_column_ui_context(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Sort from Column";
ot->description = "Change sorting to use column under cursor";
ot->idname = "FILE_OT_sort_column_ui_context";
/* api callbacks */
ot->invoke = file_column_sort_ui_context_invoke;
ot->poll = ED_operator_file_active;
ot->flag = OPTYPE_INTERNAL;
}
int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
{
wmWindowManager *wm = CTX_wm_manager(C);
@ -1713,7 +1764,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w
/* Number of items in a block (i.e. lines in a column in horizontal layout, or columns in a line
* in vertical layout).
*/
const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->columns;
const int items_block_size = is_horizontal ? sfile->layout->rows : sfile->layout->flow_columns;
/* Scroll offset is the first file in the row/column we are editing in. */
if (sfile->scroll_offset == 0) {
@ -1998,7 +2049,6 @@ void FILE_OT_directory_new(struct wmOperatorType *ot)
ot->idname = "FILE_OT_directory_new";
/* api callbacks */
ot->invoke = WM_operator_confirm;
ot->exec = file_directory_new_exec;
ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
@ -2260,10 +2310,29 @@ ARegion *file_tools_region(ScrArea *sa)
arnew->regiontype = RGN_TYPE_TOOLS;
arnew->alignment = RGN_ALIGN_LEFT;
ar = MEM_callocN(sizeof(ARegion), "tool props for file");
BLI_insertlinkafter(&sa->regionbase, arnew, ar);
ar->regiontype = RGN_TYPE_TOOL_PROPS;
ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
return arnew;
}
ARegion *file_tool_props_region(ScrArea *sa)
{
ARegion *ar, *arnew;
if ((ar = BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS)) != NULL) {
return ar;
}
/* add subdiv level; after execute region */
ar = BKE_area_find_region_type(sa, RGN_TYPE_EXECUTE);
/* is error! */
if (ar == NULL) {
return NULL;
}
arnew = MEM_callocN(sizeof(ARegion), "tool props for file");
BLI_insertlinkafter(&sa->regionbase, ar, arnew);
arnew->regiontype = RGN_TYPE_TOOL_PROPS;
arnew->alignment = RGN_ALIGN_RIGHT;
return arnew;
}
@ -2292,6 +2361,17 @@ void FILE_OT_bookmark_toggle(struct wmOperatorType *ot)
ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
}
static bool file_filenum_poll(bContext *C)
{
SpaceFile *sfile = CTX_wm_space_file(C);
if (!ED_operator_file_active(C)) {
return false;
}
return sfile->params && (sfile->params->action_type == FILE_SAVE);
}
/**
* Looks for a string of digits within name (using BLI_stringdec) and adjusts it by add.
*/
@ -2349,7 +2429,7 @@ void FILE_OT_filenum(struct wmOperatorType *ot)
/* api callbacks */
ot->exec = file_filenum_exec;
ot->poll = ED_operator_file_active; /* <- important, handler is on window level */
ot->poll = file_filenum_poll;
/* props */
RNA_def_int(ot->srna, "increment", 1, -100, 100, "Increment", "", -100, 100);

View File

@ -103,6 +103,7 @@ void file_panels_register(ARegionType *art)
strcpy(pt->idname, "FILE_PT_operator");
strcpy(pt->label, N_("Operator"));
strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
pt->flag = PNL_NO_HEADER;
pt->poll = file_panel_operator_poll;
pt->draw_header = file_panel_operator_header;
pt->draw = file_panel_operator;

View File

@ -329,25 +329,20 @@ enum {
FL_IS_PENDING = 1 << 2,
FL_NEED_SORTING = 1 << 3,
FL_NEED_FILTERING = 1 << 4,
FL_SORT_INVERT = 1 << 5,
};
#define SPECIAL_IMG_SIZE 48
#define SPECIAL_IMG_ROWS 4
#define SPECIAL_IMG_COLS 4
#define SPECIAL_IMG_SIZE 256
#define SPECIAL_IMG_ROWS 1
#define SPECIAL_IMG_COLS 6
enum {
SPECIAL_IMG_FOLDER = 0,
SPECIAL_IMG_PARENT = 1,
SPECIAL_IMG_REFRESH = 2,
SPECIAL_IMG_BLENDFILE = 3,
SPECIAL_IMG_SOUNDFILE = 4,
SPECIAL_IMG_MOVIEFILE = 5,
SPECIAL_IMG_PYTHONFILE = 6,
SPECIAL_IMG_TEXTFILE = 7,
SPECIAL_IMG_FONTFILE = 8,
SPECIAL_IMG_UNKNOWNFILE = 9,
SPECIAL_IMG_LOADING = 10,
SPECIAL_IMG_BACKUP = 11,
SPECIAL_IMG_DOCUMENT = 0,
SPECIAL_IMG_FOLDER = 1,
SPECIAL_IMG_PARENT = 2,
SPECIAL_IMG_DRIVE_FIXED = 3,
SPECIAL_IMG_DRIVE_ATTACHED = 4,
SPECIAL_IMG_DRIVE_REMOTE = 5,
SPECIAL_IMG_MAX,
};
@ -369,6 +364,19 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size);
/* ********** Sort helpers ********** */
struct FileSortData {
bool inverted;
};
static int compare_apply_inverted(int val, const struct FileSortData *sort_data)
{
return sort_data->inverted ? -val : val;
}
/**
* Handles inverted sorting itself (currently there's nothing to invert), so if this returns non-0,
* it should be used as-is and not inverted.
*/
static int compare_direntry_generic(const FileListInternEntry *entry1,
const FileListInternEntry *entry2)
{
@ -420,10 +428,11 @@ static int compare_direntry_generic(const FileListInternEntry *entry1,
return 0;
}
static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2)
static int compare_name(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
const struct FileSortData *sort_data = user_data;
char *name1, *name2;
int ret;
@ -434,13 +443,14 @@ static int compare_name(void *UNUSED(user_data), const void *a1, const void *a2)
name1 = entry1->name;
name2 = entry2->name;
return BLI_strcasecmp_natural(name1, name2);
return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2)
static int compare_date(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
const struct FileSortData *sort_data = user_data;
char *name1, *name2;
int64_t time1, time2;
int ret;
@ -452,22 +462,23 @@ static int compare_date(void *UNUSED(user_data), const void *a1, const void *a2)
time1 = (int64_t)entry1->st.st_mtime;
time2 = (int64_t)entry2->st.st_mtime;
if (time1 < time2) {
return 1;
return compare_apply_inverted(1, sort_data);
}
if (time1 > time2) {
return -1;
return compare_apply_inverted(-1, sort_data);
}
name1 = entry1->name;
name2 = entry2->name;
return BLI_strcasecmp_natural(name1, name2);
return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2)
static int compare_size(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
const struct FileSortData *sort_data = user_data;
char *name1, *name2;
uint64_t size1, size2;
int ret;
@ -479,22 +490,23 @@ static int compare_size(void *UNUSED(user_data), const void *a1, const void *a2)
size1 = entry1->st.st_size;
size2 = entry2->st.st_size;
if (size1 < size2) {
return 1;
return compare_apply_inverted(1, sort_data);
}
if (size1 > size2) {
return -1;
return compare_apply_inverted(-1, sort_data);
}
name1 = entry1->name;
name2 = entry2->name;
return BLI_strcasecmp_natural(name1, name2);
return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
static int compare_extension(void *UNUSED(user_data), const void *a1, const void *a2)
static int compare_extension(void *user_data, const void *a1, const void *a2)
{
const FileListInternEntry *entry1 = a1;
const FileListInternEntry *entry2 = a2;
const struct FileSortData *sort_data = user_data;
char *name1, *name2;
int ret;
@ -516,10 +528,10 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void
return -1;
}
if (entry1->blentype < entry2->blentype) {
return -1;
return compare_apply_inverted(-1, sort_data);
}
if (entry1->blentype > entry2->blentype) {
return 1;
return compare_apply_inverted(1, sort_data);
}
}
else {
@ -539,48 +551,58 @@ static int compare_extension(void *UNUSED(user_data), const void *a1, const void
}
if ((ret = BLI_strcasecmp(sufix1, sufix2))) {
return ret;
return compare_apply_inverted(ret, sort_data);
}
}
name1 = entry1->name;
name2 = entry2->name;
return BLI_strcasecmp_natural(name1, name2);
return compare_apply_inverted(BLI_strcasecmp_natural(name1, name2), sort_data);
}
void filelist_sort(struct FileList *filelist)
{
if ((filelist->flags & FL_NEED_SORTING) && (filelist->sort != FILE_SORT_NONE)) {
void *sort_cb = NULL;
switch (filelist->sort) {
case FILE_SORT_ALPHA:
BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_name, NULL);
sort_cb = compare_name;
break;
case FILE_SORT_TIME:
BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_date, NULL);
sort_cb = compare_date;
break;
case FILE_SORT_SIZE:
BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_size, NULL);
sort_cb = compare_size;
break;
case FILE_SORT_EXTENSION:
BLI_listbase_sort_r(&filelist->filelist_intern.entries, compare_extension, NULL);
sort_cb = compare_extension;
break;
case FILE_SORT_NONE: /* Should never reach this point! */
default:
BLI_assert(0);
break;
}
BLI_listbase_sort_r(
&filelist->filelist_intern.entries,
sort_cb,
&(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0});
filelist_filter_clear(filelist);
filelist->flags &= ~FL_NEED_SORTING;
}
}
void filelist_setsorting(struct FileList *filelist, const short sort)
void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort)
{
if (filelist->sort != sort) {
const bool was_invert_sort = filelist->flags & FL_SORT_INVERT;
if ((filelist->sort != sort) || (was_invert_sort != invert_sort)) {
filelist->sort = sort;
filelist->flags |= FL_NEED_SORTING;
filelist->flags = invert_sort ? (filelist->flags | FL_SORT_INVERT) :
(filelist->flags & ~FL_SORT_INVERT);
}
}
@ -635,9 +657,9 @@ static bool is_filtered_file(FileListInternEntry *file,
{
bool is_filtered = !is_hidden_file(file->relpath, filter);
if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) {
if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
/* We only check for types if some type are enabled in filtering. */
if (filter->filter) {
if (filter->filter && (filter->flags & FLF_DO_FILTER)) {
if (file->typeflag & FILE_TYPE_DIR) {
if (file->typeflag &
(FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
@ -657,6 +679,7 @@ static bool is_filtered_file(FileListInternEntry *file,
}
}
}
/* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
if (is_filtered && (filter->filter_search[0] != '\0')) {
if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
is_filtered = false;
@ -676,9 +699,9 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis
if (BLO_library_path_explode(path, dir, &group, &name)) {
is_filtered = !is_hidden_file(file->relpath, filter);
if (is_filtered && (filter->flags & FLF_DO_FILTER) && !FILENAME_IS_CURRPAR(file->relpath)) {
if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
/* We only check for types if some type are enabled in filtering. */
if (filter->filter || filter->filter_id) {
if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
if (file->typeflag & FILE_TYPE_DIR) {
if (file->typeflag &
(FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
@ -704,6 +727,7 @@ static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileLis
}
}
}
/* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
if (is_filtered && (filter->filter_search[0] != '\0')) {
if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
is_filtered = false;
@ -904,42 +928,12 @@ static ImBuf *filelist_geticon_image_ex(const unsigned int typeflag, const char
if (FILENAME_IS_PARENT(relpath)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PARENT];
}
else if (FILENAME_IS_CURRENT(relpath)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_REFRESH];
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_FOLDER];
}
}
else if (typeflag & FILE_TYPE_BLENDER) {
ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE];
}
else if (typeflag & FILE_TYPE_BLENDERLIB) {
ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
}
else if (typeflag & (FILE_TYPE_MOVIE)) {
ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE];
}
else if (typeflag & FILE_TYPE_SOUND) {
ibuf = gSpecialFileImages[SPECIAL_IMG_SOUNDFILE];
}
else if (typeflag & FILE_TYPE_PYSCRIPT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_PYTHONFILE];
}
else if (typeflag & FILE_TYPE_FTFONT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_FONTFILE];
}
else if (typeflag & FILE_TYPE_TEXT) {
ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
}
else if (typeflag & FILE_TYPE_IMAGE) {
ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
}
else if (typeflag & FILE_TYPE_BLENDER_BACKUP) {
ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP];
}
else {
ibuf = gSpecialFileImages[SPECIAL_IMG_UNKNOWNFILE];
ibuf = gSpecialFileImages[SPECIAL_IMG_DOCUMENT];
}
return ibuf;
@ -1001,10 +995,13 @@ static int filelist_geticon_ex(const int typeflag,
return ICON_FILE_BLANK;
}
else if (typeflag & FILE_TYPE_COLLADA) {
return ICON_FILE_BLANK;
return ICON_FILE_3D;
}
else if (typeflag & FILE_TYPE_ALEMBIC) {
return ICON_FILE_BLANK;
return ICON_FILE_3D;
}
else if (typeflag & FILE_TYPE_OBJECT_IO) {
return ICON_FILE_3D;
}
else if (typeflag & FILE_TYPE_TEXT) {
return ICON_FILE_TEXT;
@ -1243,7 +1240,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache)
BLI_task_pool_cancel(cache->previews_pool);
while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) {
// printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
// printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path,
// preview->img);
if (preview->img) {
IMB_freeImBuf(preview->img);
}
@ -2128,6 +2126,9 @@ int ED_path_extension_type(const char *path)
else if (BLI_path_extension_check(path, ".abc")) {
return FILE_TYPE_ALEMBIC;
}
else if (BLI_path_extension_check_n(path, ".obj", ".3ds", ".fbx", ".glb", ".gltf", NULL)) {
return FILE_TYPE_OBJECT_IO;
}
else if (BLI_path_extension_check_array(path, imb_ext_image)) {
return FILE_TYPE_IMAGE;
}
@ -2177,9 +2178,9 @@ int ED_file_extension_icon(const char *path)
case FILE_TYPE_BTX:
return ICON_FILE_BLANK;
case FILE_TYPE_COLLADA:
return ICON_FILE_BLANK;
case FILE_TYPE_ALEMBIC:
return ICON_FILE_BLANK;
case FILE_TYPE_OBJECT_IO:
return ICON_FILE_3D;
case FILE_TYPE_TEXT:
return ICON_FILE_TEXT;
default:

View File

@ -55,7 +55,7 @@ void folderlist_pushdir(struct ListBase *folderlist, const char *dir);
const char *folderlist_peeklastdir(struct ListBase *folderdist);
int folderlist_clear_next(struct SpaceFile *sfile);
void filelist_setsorting(struct FileList *filelist, const short sort);
void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort);
void filelist_sort(struct FileList *filelist);
void filelist_setfilter_options(struct FileList *filelist,

View File

@ -49,6 +49,8 @@
#include "BLI_utildefines.h"
#include "BLI_fnmatch.h"
#include "BLT_translation.h"
#include "BKE_appdir.h"
#include "BKE_context.h"
#include "BKE_main.h"
@ -69,6 +71,8 @@
#include "file_intern.h"
#include "filelist.h"
#define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X)
FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile)
{
if (!sfile->params) {
@ -99,6 +103,8 @@ short ED_fileselect_set_params(SpaceFile *sfile)
sfile->params->filter_glob[0] = '\0';
/* set the default thumbnails size */
sfile->params->thumbnail_size = 128;
/* Show size column by default. */
sfile->params->details_flags = FILE_DETAILS_SIZE | FILE_DETAILS_DATETIME;
}
params = sfile->params;
@ -161,6 +167,10 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->flag &= ~FILE_DIRSEL_ONLY;
}
if ((prop = RNA_struct_find_property(op->ptr, "hide_props_region"))) {
params->flag |= RNA_property_boolean_get(op->ptr, prop) ? FILE_HIDE_TOOL_PROPS : 0;
}
params->filter = 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_blender"))) {
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BLENDER : 0;
@ -261,6 +271,10 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->sort = FILE_SORT_ALPHA;
}
if ((prop = RNA_struct_find_property(op->ptr, "action_type"))) {
params->action_type = RNA_property_enum_get(op->ptr, prop);
}
if (params->display == FILE_DEFAULTDISPLAY) {
if (params->display_previous == FILE_DEFAULTDISPLAY) {
if (U.uiflag & USER_SHOW_THUMBNAILS) {
@ -268,11 +282,11 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->display = FILE_IMGDISPLAY;
}
else {
params->display = FILE_SHORTDISPLAY;
params->display = FILE_VERTICALDISPLAY;
}
}
else {
params->display = FILE_SHORTDISPLAY;
params->display = FILE_VERTICALDISPLAY;
}
}
else {
@ -293,7 +307,7 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->type = FILE_UNIX;
params->flag |= FILE_HIDE_DOT;
params->flag &= ~FILE_DIRSEL_ONLY;
params->display = FILE_SHORTDISPLAY;
params->display = FILE_VERTICALDISPLAY;
params->display_previous = FILE_DEFAULTDISPLAY;
params->sort = FILE_SORT_ALPHA;
params->filter = 0;
@ -344,7 +358,7 @@ void ED_fileselect_reset_params(SpaceFile *sfile)
void fileselect_file_set(SpaceFile *sfile, const int index)
{
const struct FileDirEntry *file = filelist_file(sfile->files, index);
if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_FOLDER)) {
if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_DIR)) {
BLI_strncpy(sfile->params->file, file->relpath, FILE_MAXFILE);
}
}
@ -372,10 +386,10 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *ar)
}
else {
const int y_item = layout->tile_h + (2 * layout->tile_border_y);
const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur));
const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur)) - layout->offset_top;
const int y_over = y_item - (y_view % y_item);
numfiles = (int)((float)(y_view + y_over) / (float)(y_item));
return numfiles * layout->columns;
return numfiles * layout->flow_columns;
}
}
@ -395,19 +409,19 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r
}
colmin = (rect->xmin) / (layout->tile_w + 2 * layout->tile_border_x);
rowmin = (rect->ymin) / (layout->tile_h + 2 * layout->tile_border_y);
rowmin = (rect->ymin - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
colmax = (rect->xmax) / (layout->tile_w + 2 * layout->tile_border_x);
rowmax = (rect->ymax) / (layout->tile_h + 2 * layout->tile_border_y);
rowmax = (rect->ymax - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
if (is_inside(colmin, rowmin, layout->columns, layout->rows) ||
is_inside(colmax, rowmax, layout->columns, layout->rows)) {
CLAMP(colmin, 0, layout->columns - 1);
if (is_inside(colmin, rowmin, layout->flow_columns, layout->rows) ||
is_inside(colmax, rowmax, layout->flow_columns, layout->rows)) {
CLAMP(colmin, 0, layout->flow_columns - 1);
CLAMP(rowmin, 0, layout->rows - 1);
CLAMP(colmax, 0, layout->columns - 1);
CLAMP(colmax, 0, layout->flow_columns - 1);
CLAMP(rowmax, 0, layout->rows - 1);
}
if ((colmin > layout->columns - 1) || (rowmin > layout->rows - 1)) {
if ((colmin > layout->flow_columns - 1) || (rowmin > layout->rows - 1)) {
sel.first = -1;
}
else {
@ -415,10 +429,10 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r
sel.first = layout->rows * colmin + rowmin;
}
else {
sel.first = colmin + layout->columns * rowmin;
sel.first = colmin + layout->flow_columns * rowmin;
}
}
if ((colmax > layout->columns - 1) || (rowmax > layout->rows - 1)) {
if ((colmax > layout->flow_columns - 1) || (rowmax > layout->rows - 1)) {
sel.last = -1;
}
else {
@ -426,7 +440,7 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r
sel.last = layout->rows * colmax + rowmax;
}
else {
sel.last = colmax + layout->columns * rowmax;
sel.last = colmax + layout->flow_columns * rowmax;
}
}
@ -443,9 +457,9 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y)
}
offsetx = (x) / (layout->tile_w + 2 * layout->tile_border_x);
offsety = (y) / (layout->tile_h + 2 * layout->tile_border_y);
offsety = (y - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
if (offsetx > layout->columns - 1) {
if (offsetx > layout->flow_columns - 1) {
return -1;
}
if (offsety > layout->rows - 1) {
@ -456,27 +470,123 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y)
active_file = layout->rows * offsetx + offsety;
}
else {
active_file = offsetx + layout->columns * offsety;
active_file = offsetx + layout->flow_columns * offsety;
}
return active_file;
}
/**
* Get the currently visible bounds of the layout in screen space. Matches View2D.mask minus the
* top column-header row.
*/
void ED_fileselect_layout_maskrect(const FileLayout *layout, const View2D *v2d, rcti *r_rect)
{
*r_rect = v2d->mask;
r_rect->ymax -= layout->offset_top;
}
bool ED_fileselect_layout_is_inside_pt(const FileLayout *layout, const View2D *v2d, int x, int y)
{
rcti maskrect;
ED_fileselect_layout_maskrect(layout, v2d, &maskrect);
return BLI_rcti_isect_pt(&maskrect, x, y);
}
bool ED_fileselect_layout_isect_rect(const FileLayout *layout,
const View2D *v2d,
const rcti *rect,
rcti *r_dst)
{
rcti maskrect;
ED_fileselect_layout_maskrect(layout, v2d, &maskrect);
return BLI_rcti_isect(&maskrect, rect, r_dst);
}
void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y)
{
if (layout->flag == FILE_LAYOUT_HOR) {
*x = layout->tile_border_x +
(tile / layout->rows) * (layout->tile_w + 2 * layout->tile_border_x);
*y = layout->tile_border_y +
*y = layout->offset_top + layout->tile_border_y +
(tile % layout->rows) * (layout->tile_h + 2 * layout->tile_border_y);
}
else {
*x = layout->tile_border_x +
((tile) % layout->columns) * (layout->tile_w + 2 * layout->tile_border_x);
*y = layout->tile_border_y +
((tile) / layout->columns) * (layout->tile_h + 2 * layout->tile_border_y);
((tile) % layout->flow_columns) * (layout->tile_w + 2 * layout->tile_border_x);
*y = layout->offset_top + layout->tile_border_y +
((tile) / layout->flow_columns) * (layout->tile_h + 2 * layout->tile_border_y);
}
}
/**
* Check if the region coordinate defined by \a x and \a y are inside the column header.
*/
bool file_attribute_column_header_is_inside(const View2D *v2d,
const FileLayout *layout,
int x,
int y)
{
rcti header_rect = v2d->mask;
header_rect.ymin = header_rect.ymax - layout->attribute_column_header_h;
return BLI_rcti_isect_pt(&header_rect, x, y);
}
bool file_attribute_column_type_enabled(const FileSelectParams *params,
FileAttributeColumnType column)
{
switch (column) {
case COLUMN_NAME:
/* Always enabled */
return true;
case COLUMN_DATETIME:
return (params->details_flags & FILE_DETAILS_DATETIME) != 0;
case COLUMN_SIZE:
return (params->details_flags & FILE_DETAILS_SIZE) != 0;
default:
return false;
}
}
/**
* Find the column type at region coordinate given by \a x (y doesn't matter for this).
*/
FileAttributeColumnType file_attribute_column_type_find_isect(const View2D *v2d,
const FileSelectParams *params,
FileLayout *layout,
int x)
{
float mx, my;
int offset_tile;
UI_view2d_region_to_view(v2d, x, v2d->mask.ymax - layout->offset_top - 1, &mx, &my);
offset_tile = ED_fileselect_layout_offset(
layout, (int)(v2d->tot.xmin + mx), (int)(v2d->tot.ymax - my));
if (offset_tile > -1) {
int tile_x, tile_y;
int pos_x = 0;
int rel_x; /* x relative to the hovered tile */
ED_fileselect_layout_tilepos(layout, offset_tile, &tile_x, &tile_y);
/* Column header drawing doesn't use left tile border, so subtract it. */
rel_x = mx - (tile_x - layout->tile_border_x);
for (FileAttributeColumnType column = 0; column < ATTRIBUTE_COLUMN_MAX; column++) {
if (!file_attribute_column_type_enabled(params, column)) {
continue;
}
const int width = layout->attribute_columns[column].width;
if (IN_RANGE(rel_x, pos_x, pos_x + width)) {
return column;
}
pos_x += width;
}
}
return COLUMN_NONE;
}
float file_string_width(const char *str)
{
uiStyle *style = UI_style_get();
@ -512,20 +622,52 @@ float file_font_pointsize(void)
#endif
}
static void column_widths(FileSelectParams *params, struct FileLayout *layout)
static void file_attribute_columns_widths(const FileSelectParams *params, FileLayout *layout)
{
int i;
FileAttributeColumn *columns = layout->attribute_columns;
const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
const int pad = small_size ? 0 : ATTRIBUTE_COLUMN_PADDING * 2;
for (i = 0; i < MAX_FILE_COLUMN; ++i) {
layout->column_widths[i] = 0;
for (int i = 0; i < ATTRIBUTE_COLUMN_MAX; ++i) {
layout->attribute_columns[i].width = 0;
}
layout->column_widths[COLUMN_NAME] = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X;
/* Biggest possible reasonable values... */
layout->column_widths[COLUMN_DATE] = file_string_width(small_size ? "23/08/89" : "23-Dec-89");
layout->column_widths[COLUMN_TIME] = file_string_width("23:59");
layout->column_widths[COLUMN_SIZE] = file_string_width(small_size ? "98.7 M" : "98.7 MiB");
columns[COLUMN_DATETIME].width = file_string_width(small_size ? "23/08/89" :
"23 Dec 6789, 23:59") +
pad;
columns[COLUMN_SIZE].width = file_string_width(small_size ? "98.7 M" : "098.7 MB") + pad;
if (params->display == FILE_IMGDISPLAY) {
columns[COLUMN_NAME].width = ((float)params->thumbnail_size / 8.0f) * UI_UNIT_X;
}
/* Name column uses remaining width */
else {
int remwidth = layout->tile_w;
for (FileAttributeColumnType column_type = ATTRIBUTE_COLUMN_MAX - 1; column_type >= 0;
column_type--) {
if ((column_type == COLUMN_NAME) ||
!file_attribute_column_type_enabled(params, column_type)) {
continue;
}
remwidth -= columns[column_type].width;
}
columns[COLUMN_NAME].width = remwidth;
}
}
static void file_attribute_columns_init(const FileSelectParams *params, FileLayout *layout)
{
file_attribute_columns_widths(params, layout);
layout->attribute_columns[COLUMN_NAME].name = N_("Name");
layout->attribute_columns[COLUMN_NAME].sort_type = FILE_SORT_ALPHA;
layout->attribute_columns[COLUMN_NAME].text_align = UI_STYLE_TEXT_LEFT;
layout->attribute_columns[COLUMN_DATETIME].name = N_("Date Modified");
layout->attribute_columns[COLUMN_DATETIME].sort_type = FILE_SORT_TIME;
layout->attribute_columns[COLUMN_DATETIME].text_align = UI_STYLE_TEXT_LEFT;
layout->attribute_columns[COLUMN_SIZE].name = N_("Size");
layout->attribute_columns[COLUMN_SIZE].sort_type = FILE_SORT_SIZE;
layout->attribute_columns[COLUMN_SIZE].text_align = UI_STYLE_TEXT_RIGHT;
}
void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
@ -533,7 +675,6 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
FileSelectParams *params = ED_fileselect_get_params(sfile);
FileLayout *layout = NULL;
View2D *v2d = &ar->v2d;
int maxlen = 0;
int numfiles;
int textheight;
@ -560,57 +701,66 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar)
layout->tile_w = layout->prv_w + 2 * layout->prv_border_x;
layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight;
layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x);
layout->columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x);
if (layout->columns > 0) {
layout->rows = numfiles / layout->columns + 1; // XXX dirty, modulo is zero
layout->flow_columns = layout->width / (layout->tile_w + 2 * layout->tile_border_x);
layout->attribute_column_header_h = 0;
layout->offset_top = 0;
if (layout->flow_columns > 0) {
layout->rows = numfiles / layout->flow_columns + 1; // XXX dirty, modulo is zero
}
else {
layout->columns = 1;
layout->flow_columns = 1;
layout->rows = numfiles + 1; // XXX dirty, modulo is zero
}
layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) +
layout->tile_border_y * 2;
layout->tile_border_y * 2 - layout->offset_top;
layout->flag = FILE_LAYOUT_VER;
}
else {
int column_space = 0.6f * UI_UNIT_X;
int column_icon_space = 0.2f * UI_UNIT_X;
else if (params->display == FILE_VERTICALDISPLAY) {
int rowcount;
layout->prv_w = 0;
layout->prv_h = 0;
layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X;
layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y;
layout->tile_border_x = 0.4f * UI_UNIT_X;
layout->tile_border_y = 0.1f * UI_UNIT_Y;
layout->prv_border_x = 0;
layout->prv_border_y = 0;
layout->tile_h = textheight * 3 / 2;
layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x);
layout->tile_w = layout->width;
layout->flow_columns = 1;
layout->attribute_column_header_h = layout->tile_h * 1.2f + 2 * layout->tile_border_y;
layout->offset_top = layout->attribute_column_header_h;
rowcount = (int)(BLI_rctf_size_y(&v2d->cur) - layout->offset_top - 2 * layout->tile_border_y) /
(layout->tile_h + 2 * layout->tile_border_y);
file_attribute_columns_init(params, layout);
layout->rows = MAX2(rowcount, numfiles);
BLI_assert(layout->rows != 0);
layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) +
layout->tile_border_y * 2 + layout->offset_top;
layout->flag = FILE_LAYOUT_VER;
}
else if (params->display == FILE_HORIZONTALDISPLAY) {
layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X;
layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y;
layout->tile_border_x = 0.4f * UI_UNIT_X;
layout->tile_border_y = 0.1f * UI_UNIT_Y;
layout->tile_h = textheight * 3 / 2;
layout->attribute_column_header_h = 0;
layout->offset_top = layout->attribute_column_header_h;
layout->height = (int)(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y);
/* Padding by full scrollbar H is too much, can overlap tile border Y. */
layout->rows = (layout->height - V2D_SCROLL_HEIGHT + layout->tile_border_y) /
(layout->tile_h + 2 * layout->tile_border_y);
layout->tile_w = VERTLIST_MAJORCOLUMN_WIDTH;
file_attribute_columns_init(params, layout);
column_widths(params, layout);
if (params->display == FILE_SHORTDISPLAY) {
maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
(int)layout->column_widths[COLUMN_NAME] + column_space +
(int)layout->column_widths[COLUMN_SIZE] + column_space;
}
else {
maxlen = ICON_DEFAULT_WIDTH_SCALE + column_icon_space +
(int)layout->column_widths[COLUMN_NAME] + column_space +
(int)layout->column_widths[COLUMN_DATE] + column_space +
(int)layout->column_widths[COLUMN_TIME] + column_space +
(int)layout->column_widths[COLUMN_SIZE] + column_space;
}
layout->tile_w = maxlen;
if (layout->rows > 0) {
layout->columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero
layout->flow_columns = numfiles / layout->rows + 1; // XXX dirty, modulo is zero
}
else {
layout->rows = 1;
layout->columns = numfiles + 1; // XXX dirty, modulo is zero
layout->flow_columns = numfiles + 1; // XXX dirty, modulo is zero
}
layout->width = sfile->layout->columns * (layout->tile_w + 2 * layout->tile_border_x) +
layout->width = sfile->layout->flow_columns * (layout->tile_w + 2 * layout->tile_border_x) +
layout->tile_border_x * 2;
layout->flag = FILE_LAYOUT_HOR;
}

View File

@ -72,23 +72,40 @@ static SpaceLink *file_new(const ScrArea *UNUSED(area), const Scene *UNUSED(scen
/* Ignore user preference "USER_HEADER_BOTTOM" here (always show top for new types). */
ar->alignment = RGN_ALIGN_TOP;
/* Tools region */
ar = MEM_callocN(sizeof(ARegion), "tools region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_TOOLS;
ar->alignment = RGN_ALIGN_LEFT;
/* Tool props (aka operator) region */
ar = MEM_callocN(sizeof(ARegion), "tool props region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_TOOL_PROPS;
ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
/* ui list region */
ar = MEM_callocN(sizeof(ARegion), "ui region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_UI;
ar->alignment = RGN_ALIGN_TOP;
ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
/* Tools region */
ar = MEM_callocN(sizeof(ARegion), "tools region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_TOOLS;
ar->alignment = RGN_ALIGN_LEFT;
/* Tools region (lower split region) */
ar = MEM_callocN(sizeof(ARegion), "lower tools region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_TOOLS;
ar->alignment = RGN_ALIGN_BOTTOM | RGN_SPLIT_PREV;
ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
/* Execute region */
ar = MEM_callocN(sizeof(ARegion), "execute region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_EXECUTE;
ar->alignment = RGN_ALIGN_BOTTOM;
ar->flag |= RGN_FLAG_DYNAMIC_SIZE;
/* Tool props region is added as needed. */
#if 0
/* Tool props (aka operator) region */
ar = MEM_callocN(sizeof(ARegion), "tool props region for file");
BLI_addtail(&sfile->regionbase, ar);
ar->regiontype = RGN_TYPE_TOOL_PROPS;
ar->alignment = RGN_ALIGN_RIGHT;
#endif
/* main region */
ar = MEM_callocN(sizeof(ARegion), "main region for file");
@ -204,6 +221,7 @@ static SpaceLink *file_duplicate(SpaceLink *sl)
static void file_refresh(const bContext *C, ScrArea *sa)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_params(sfile);
struct FSMenu *fsmenu = ED_fsmenu_get();
@ -217,15 +235,16 @@ static void file_refresh(const bContext *C, ScrArea *sa)
}
filelist_setdir(sfile->files, params->dir);
filelist_setrecursion(sfile->files, params->recursion_level);
filelist_setsorting(sfile->files, params->sort);
filelist_setfilter_options(sfile->files,
(params->flag & FILE_FILTER) != 0,
(params->flag & FILE_HIDE_DOT) != 0,
false, /* TODO hide_parent, should be controllable? */
params->filter,
params->filter_id,
params->filter_glob,
params->filter_search);
filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT);
filelist_setfilter_options(
sfile->files,
(params->flag & FILE_FILTER) != 0,
(params->flag & FILE_HIDE_DOT) != 0,
true, /* Just always hide parent, prefer to not add an extra user option for this. */
params->filter,
params->filter_id,
params->filter_glob,
params->filter_search);
/* Update the active indices of bookmarks & co. */
sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir);
@ -254,7 +273,7 @@ static void file_refresh(const bContext *C, ScrArea *sa)
else {
filelist_cache_previews_set(sfile->files, false);
if (sfile->previews_timer) {
WM_event_remove_timer_notifier(wm, CTX_wm_window(C), sfile->previews_timer);
WM_event_remove_timer_notifier(wm, win, sfile->previews_timer);
sfile->previews_timer = NULL;
}
}
@ -269,10 +288,20 @@ static void file_refresh(const bContext *C, ScrArea *sa)
/* Might be called with NULL sa, see file_main_region_draw() below. */
if (sa && BKE_area_find_region_type(sa, RGN_TYPE_TOOLS) == NULL) {
/* Create TOOLS/TOOL_PROPS regions. */
/* Create TOOLS region. */
file_tools_region(sa);
ED_area_initialize(wm, CTX_wm_window(C), sa);
ED_area_initialize(wm, win, sa);
}
if (sa && sfile->op && BKE_area_find_region_type(sa, RGN_TYPE_TOOL_PROPS) == NULL) {
/* Create TOOL_PROPS region. */
ARegion *region_props = file_tool_props_region(sa);
if (params->flag & FILE_HIDE_TOOL_PROPS) {
region_props->flag |= RGN_FLAG_HIDDEN;
}
ED_area_initialize(wm, win, sa);
}
ED_area_tag_redraw(sa);
@ -406,6 +435,11 @@ static void file_main_region_draw(const bContext *C, ARegion *ar)
v2d->keepofs &= ~V2D_LOCKOFS_Y;
v2d->keepofs |= V2D_LOCKOFS_X;
}
else if (params->display == FILE_VERTICALDISPLAY) {
v2d->scroll = V2D_SCROLL_RIGHT;
v2d->keepofs &= ~V2D_LOCKOFS_Y;
v2d->keepofs |= V2D_LOCKOFS_X;
}
else {
v2d->scroll = V2D_SCROLL_BOTTOM;
v2d->keepofs &= ~V2D_LOCKOFS_X;
@ -439,7 +473,9 @@ static void file_main_region_draw(const bContext *C, ARegion *ar)
UI_view2d_view_restore(C);
/* scrollers */
scrollers = UI_view2d_scrollers_calc(v2d, NULL);
rcti view_rect;
ED_fileselect_layout_maskrect(sfile->layout, v2d, &view_rect);
scrollers = UI_view2d_scrollers_calc(v2d, &view_rect);
UI_view2d_scrollers_draw(v2d, scrollers);
UI_view2d_scrollers_free(scrollers);
}
@ -452,6 +488,7 @@ static void file_operatortypes(void)
WM_operatortype_append(FILE_OT_select_box);
WM_operatortype_append(FILE_OT_select_bookmark);
WM_operatortype_append(FILE_OT_highlight);
WM_operatortype_append(FILE_OT_sort_column_ui_context);
WM_operatortype_append(FILE_OT_execute);
WM_operatortype_append(FILE_OT_cancel);
WM_operatortype_append(FILE_OT_parent);
@ -538,7 +575,8 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar)
{
wmKeyMap *keymap;
UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_HEADER, ar->winx, ar->winy);
ED_region_panels_init(wm, ar);
ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
/* own keymap */
keymap = WM_keymap_ensure(wm->defaultconf, "File Browser", SPACE_FILE, 0);
@ -550,22 +588,18 @@ static void file_ui_region_init(wmWindowManager *wm, ARegion *ar)
static void file_ui_region_draw(const bContext *C, ARegion *ar)
{
float col[3];
/* clear */
UI_GetThemeColor3fv(TH_BACK, col);
GPU_clear_color(col[0], col[1], col[2], 0.0);
GPU_clear(GPU_COLOR_BIT);
ED_region_panels(C, ar);
}
/* scrolling here is just annoying, disable it */
ar->v2d.cur.ymax = BLI_rctf_size_y(&ar->v2d.cur);
ar->v2d.cur.ymin = 0;
static void file_execution_region_init(wmWindowManager *wm, ARegion *ar)
{
ED_region_panels_init(wm, ar);
ar->v2d.keepzoom |= V2D_LOCKZOOM_X | V2D_LOCKZOOM_Y;
}
/* set view2d view matrix for scrolling (without scrollers) */
UI_view2d_view_ortho(&ar->v2d);
file_draw_buttons(C, ar);
UI_view2d_view_restore(C);
static void file_execution_region_draw(const bContext *C, ARegion *ar)
{
ED_region_panels(C, ar);
}
static void file_ui_region_listener(wmWindow *UNUSED(win),
@ -656,13 +690,21 @@ void ED_spacetype_file(void)
/* regions: ui */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
art->regionid = RGN_TYPE_UI;
art->prefsizey = 60;
art->keymapflag = ED_KEYMAP_UI;
art->listener = file_ui_region_listener;
art->init = file_ui_region_init;
art->draw = file_ui_region_draw;
BLI_addhead(&st->regiontypes, art);
/* regions: execution */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
art->regionid = RGN_TYPE_EXECUTE;
art->keymapflag = ED_KEYMAP_UI;
art->listener = file_ui_region_listener;
art->init = file_execution_region_init;
art->draw = file_execution_region_draw;
BLI_addhead(&st->regiontypes, art);
/* regions: channels (directories) */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
art->regionid = RGN_TYPE_TOOLS;
@ -677,8 +719,8 @@ void ED_spacetype_file(void)
/* regions: tool properties */
art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region");
art->regionid = RGN_TYPE_TOOL_PROPS;
art->prefsizex = 0;
art->prefsizey = 360;
art->prefsizex = 240;
art->prefsizey = 60;
art->keymapflag = ED_KEYMAP_UI;
art->listener = file_tools_region_listener;
art->init = file_tools_region_init;

View File

@ -1538,7 +1538,7 @@ void GRAPH_OT_sound_bake(wmOperatorType *ot)
FILE_TYPE_FOLDER | FILE_TYPE_SOUND | FILE_TYPE_MOVIE,
FILE_SPECIAL,
FILE_OPENFILE,
WM_FILESEL_FILEPATH,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
FILE_SORT_ALPHA);
RNA_def_float(ot->srna,

View File

@ -677,8 +677,11 @@ typedef struct FileSelectParams {
/** Display mode flag. */
short display;
short display_previous;
/** Details toggles (file size, creation date, etc.) */
char details_flags;
/* The type of file action (opening or saving) */
char action_type; /* eFileSel_Action */
/** Filter when (flags & FILE_FILTER) is true. */
char _pad2[2];
int filter;
/** Max number of levels in dirtree to show at once, 0 to disable recursion. */
@ -736,8 +739,8 @@ typedef struct SpaceFile {
/* FileSelectParams.display */
enum eFileDisplayType {
FILE_DEFAULTDISPLAY = 0,
FILE_SHORTDISPLAY = 1,
FILE_LONGDISPLAY = 2,
FILE_VERTICALDISPLAY = 1,
FILE_HORIZONTALDISPLAY = 2,
FILE_IMGDISPLAY = 3,
};
@ -750,6 +753,12 @@ enum eFileSortType {
FILE_SORT_SIZE = 4,
};
/* FileSelectParams.details_flags */
enum eFileDetails {
FILE_DETAILS_SIZE = (1 << 0),
FILE_DETAILS_DATETIME = (1 << 1),
};
/* these values need to be hardcoded in structs, dna does not recognize defines */
/* also defined in BKE */
#define FILE_MAXDIR 768
@ -787,6 +796,8 @@ typedef enum eFileSel_Params_Flag {
FILE_FILTER = (1 << 8),
FILE_PARAMS_FLAG_UNUSED_9 = (1 << 9), /* cleared */
FILE_GROUP_INSTANCE = (1 << 10),
FILE_SORT_INVERT = (1 << 11),
FILE_HIDE_TOOL_PROPS = (1 << 12)
} eFileSel_Params_Flag;
/* sfile->params->rename_flag */
@ -824,6 +835,8 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_OPERATOR = (1 << 14),
FILE_TYPE_APPLICATIONBUNDLE = (1 << 15),
FILE_TYPE_ALEMBIC = (1 << 16),
/** For all kinds of recognized import/export formats. No need for specialized types. */
FILE_TYPE_OBJECT_IO = (1 << 17),
/** An FS directory (i.e. S_ISDIR on its path is true). */
FILE_TYPE_DIR = (1 << 30),
@ -882,8 +895,7 @@ typedef struct FileDirEntryRevision {
int64_t time;
/* Temp caching of UI-generated strings... */
char size_str[16];
char time_str[8];
char date_str[16];
char datetime_str[16 + 8];
} FileDirEntryRevision;
/* Container for a variant, only relevant in asset context.

View File

@ -420,19 +420,19 @@ const EnumPropertyItem rna_enum_file_sort_items[] = {
{FILE_SORT_ALPHA,
"FILE_SORT_ALPHA",
ICON_SORTALPHA,
"Sort alphabetically",
"Name",
"Sort the file list alphabetically"},
{FILE_SORT_EXTENSION,
"FILE_SORT_EXTENSION",
ICON_SORTBYEXT,
"Sort by extension",
"Extension",
"Sort the file list by extension/type"},
{FILE_SORT_TIME,
"FILE_SORT_TIME",
ICON_SORTTIME,
"Sort by time",
"Modified Date",
"Sort files by modification time"},
{FILE_SORT_SIZE, "FILE_SORT_SIZE", ICON_SORTSIZE, "Sort by size", "Sort files by size"},
{FILE_SORT_SIZE, "FILE_SORT_SIZE", ICON_SORTSIZE, "Size", "Sort files by size"},
{0, NULL, 0, NULL, NULL},
};
@ -2142,6 +2142,18 @@ static void rna_SpaceClipEditor_view_type_update(Main *UNUSED(bmain),
/* File browser. */
int rna_FileSelectParams_filename_editable(struct PointerRNA *ptr, const char **r_info)
{
FileSelectParams *params = ptr->data;
if (params && (params->flag & FILE_DIRSEL_ONLY)) {
*r_info = "Only directories can be chosen for the current operation.";
return 0;
}
return params ? PROP_EDITABLE : 0;
}
static bool rna_FileSelectParams_use_lib_get(PointerRNA *ptr)
{
FileSelectParams *params = ptr->data;
@ -5133,25 +5145,25 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
PropertyRNA *prop;
static const EnumPropertyItem file_display_items[] = {
{FILE_SHORTDISPLAY,
"LIST_SHORT",
ICON_SHORTDISPLAY,
"Short List",
"Display files as short list"},
{FILE_LONGDISPLAY,
"LIST_LONG",
{FILE_VERTICALDISPLAY,
"LIST_VERTICAL",
ICON_LONGDISPLAY,
"Long List",
"Display files as a detailed list"},
"Vertical List",
"Display files as a vertical list"},
{FILE_HORIZONTALDISPLAY,
"LIST_HORIZONTAL",
ICON_SHORTDISPLAY,
"Horizontal List",
"Display files as a horizontal list"},
{FILE_IMGDISPLAY, "THUMBNAIL", ICON_IMGDISPLAY, "Thumbnails", "Display files as thumbnails"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem display_size_items[] = {
{32, "TINY", 0, "Tiny", ""},
{64, "SMALL", 0, "Small", ""},
{64, "TINY", 0, "Tiny", ""},
{96, "SMALL", 0, "Small", ""},
{128, "NORMAL", 0, "Regular", ""},
{256, "LARGE", 0, "Large", ""},
{192, "LARGE", 0, "Large", ""},
{0, NULL, 0, NULL, NULL},
};
@ -5267,7 +5279,10 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Title", "Title for the file browser");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
prop = RNA_def_property(srna, "directory", PROP_STRING, PROP_DIRPATH);
/* Use BYTESTRING rather than DIRPATH as subtype so UI code doesn't add OT_directory_browse
* button when displaying this prop in the file browser (it would just open a file browser). That
* should be the only effective difference between the two. */
prop = RNA_def_property(srna, "directory", PROP_STRING, PROP_BYTESTRING);
RNA_def_property_string_sdna(prop, NULL, "dir");
RNA_def_property_ui_text(prop, "Directory", "Directory displayed in the file browser");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
@ -5275,6 +5290,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
prop = RNA_def_property(srna, "filename", PROP_STRING, PROP_FILENAME);
RNA_def_property_string_sdna(prop, NULL, "file");
RNA_def_property_ui_text(prop, "File Name", "Active file in the file browser");
RNA_def_property_editable_func(prop, "rna_FileSelectParams_filename_editable");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "use_library_browsing", PROP_BOOLEAN, PROP_NONE);
@ -5295,6 +5311,19 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Recursion", "Numbers of dirtree levels to show simultaneously");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "show_details_size", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "details_flags", FILE_DETAILS_SIZE);
RNA_def_property_ui_text(prop, "File Size", "Draw a column listing the size of each file");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "show_details_datetime", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "details_flags", FILE_DETAILS_DATETIME);
RNA_def_property_ui_text(
prop,
"File Modification Date",
"Draw a column listing the date and time of modification for each file");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "use_filter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_FILTER);
RNA_def_property_ui_text(prop, "Filter Files", "Enable filtering of files");
@ -5311,6 +5340,12 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Sort", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "use_sort_invert", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_SORT_INVERT);
RNA_def_property_ui_text(
prop, "Reverse Sorting", "Sort items descending, from highest value to lowest");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
prop = RNA_def_property(srna, "use_filter_image", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "filter", FILE_TYPE_IMAGE);
RNA_def_property_ui_text(prop, "Filter Images", "Show image files");

View File

@ -159,7 +159,7 @@ enum {
WM_WINDOW_USERPREFS,
WM_WINDOW_DRIVERS,
WM_WINDOW_INFO,
// WM_WINDOW_FILESEL // UNUSED
WM_WINDOW_FILESEL,
};
struct wmWindow *WM_window_open(struct bContext *C, const struct rcti *rect);
@ -493,6 +493,8 @@ bool WM_operator_properties_checker_interval_test(const struct CheckerIntervalPa
#define WM_FILESEL_FILENAME (1 << 2)
#define WM_FILESEL_FILEPATH (1 << 3)
#define WM_FILESEL_FILES (1 << 4)
/* Show the properties sidebar by default. */
#define WM_FILESEL_SHOW_PROPS (1 << 5)
/* operator as a python command (resultuing string must be freed) */
char *WM_operator_pystring_ex(struct bContext *C,

View File

@ -2339,46 +2339,32 @@ static int wm_handler_fileselect_do(bContext *C,
switch (val) {
case EVT_FILESELECT_FULL_OPEN: {
ScrArea *sa;
wmWindow *win = CTX_wm_window(C);
const int sizex = 1020 * UI_DPI_FAC;
const int sizey = 600 * UI_DPI_FAC;
/* sa can be null when window A is active, but mouse is over window B
* in this case, open file select in original window A. Also don't
* use global areas. */
if (handler->context.area == NULL || ED_area_is_global(handler->context.area)) {
bScreen *screen = CTX_wm_screen(C);
sa = (ScrArea *)screen->areabase.first;
if (WM_window_open_temp(
C, win->sizex / 2, win->sizey / 2, sizex, sizey, WM_WINDOW_FILESEL) != NULL) {
ScrArea *area = CTX_wm_area(C);
ARegion *region_header = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
BLI_assert(area->spacetype == SPACE_FILE);
region_header->flag |= RGN_FLAG_HIDDEN;
/* Header on bottom, AZone triangle to toggle header looks misplaced at the top */
region_header->alignment = RGN_ALIGN_BOTTOM;
/* settings for filebrowser, sfile is not operator owner but sends events */
sfile = (SpaceFile *)area->spacedata.first;
sfile->op = handler->op;
ED_fileselect_set_params(sfile);
}
else {
sa = handler->context.area;
BKE_report(&wm->reports, RPT_ERROR, "Failed to open window!");
return OPERATOR_CANCELLED;
}
if (sa->full) {
/* ensure the first area becomes the file browser, because the second one is the small
* top (info-)area which might be too small (in fullscreens we have max two areas) */
if (sa->prev) {
sa = sa->prev;
}
ED_area_newspace(C, sa, SPACE_FILE, true); /* 'sa' is modified in-place */
/* we already had a fullscreen here -> mark new space as a stacked fullscreen */
sa->flag |= (AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE);
}
else if (sa->spacetype == SPACE_FILE) {
sa = ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED);
}
else {
sa = ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
}
/* note, getting the 'sa' back from the context causes a nasty bug where the newly created
* 'sa' != CTX_wm_area(C). removed the line below and set 'sa' in the 'if' above */
/* sa = CTX_wm_area(C); */
/* settings for filebrowser, sfile is not operator owner but sends events */
sfile = (SpaceFile *)sa->spacedata.first;
sfile->op = handler->op;
ED_fileselect_set_params(sfile);
action = WM_HANDLER_BREAK;
break;
}
@ -2390,14 +2376,27 @@ static int wm_handler_fileselect_do(bContext *C,
BLI_remlink(handlers, handler);
if (val != EVT_FILESELECT_EXTERNAL_CANCEL) {
ScrArea *sa = CTX_wm_area(C);
for (wmWindow *win = wm->windows.first; win; win = win->next) {
if (WM_window_is_temp_screen(win)) {
bScreen *screen = WM_window_get_active_screen(win);
ScrArea *file_sa = screen->areabase.first;
if (sa->full) {
ED_screen_full_prevspace(C, sa);
}
/* user may have left fullscreen */
else {
ED_area_prevspace(C, sa);
BLI_assert(file_sa->spacetype == SPACE_FILE);
if (BLI_listbase_is_single(&file_sa->spacedata)) {
wmWindow *ctx_win = CTX_wm_window(C);
wm_window_close(C, wm, win);
CTX_wm_window_set(C, ctx_win); // wm_window_close() NULLs.
}
else if (file_sa->full) {
ED_screen_full_prevspace(C, file_sa);
}
else {
ED_area_prevspace(C, file_sa);
}
break;
}
}
}

View File

@ -1205,6 +1205,8 @@ static ImBuf *blend_file_thumb(const bContext *C,
/* will be scaled down, but gives some nice oversampling */
ImBuf *ibuf;
BlendThumbnail *thumb;
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *windrawable_old = wm->windrawable;
char err_out[256] = "unknown";
/* screen if no camera found */
@ -1238,6 +1240,9 @@ static ImBuf *blend_file_thumb(const bContext *C,
/* gets scaled to BLEN_THUMB_SIZE */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
/* Offscreen drawing requires a drawable window context. */
wm_window_make_drawable(wm, CTX_wm_window(C));
if (scene->camera) {
ibuf = ED_view3d_draw_offscreen_imbuf_simple(depsgraph,
scene,
@ -1270,6 +1275,14 @@ static ImBuf *blend_file_thumb(const bContext *C,
err_out);
}
/* Reset to old drawable. */
if (windrawable_old) {
wm_window_make_drawable(wm, windrawable_old);
}
else {
wm_window_clear_drawable(wm);
}
if (ibuf) {
float aspect = (scene->r.xsch * scene->r.xasp) / (scene->r.ysch * scene->r.yasp);
@ -2322,7 +2335,7 @@ static void wm_open_mainfile_ui(bContext *UNUSED(C), wmOperator *op)
void WM_OT_open_mainfile(wmOperatorType *ot)
{
ot->name = "Open Blender File";
ot->name = "Open";
ot->idname = "WM_OT_open_mainfile";
ot->description = "Open a Blender file";
@ -2504,7 +2517,7 @@ void WM_OT_recover_auto_save(wmOperatorType *ot)
FILE_BLENDER,
FILE_OPENFILE,
WM_FILESEL_FILEPATH,
FILE_LONGDISPLAY,
FILE_HORIZONTALDISPLAY,
FILE_SORT_TIME);
}
@ -2638,7 +2651,7 @@ void WM_OT_save_as_mainfile(wmOperatorType *ot)
{
PropertyRNA *prop;
ot->name = "Save As Blender File";
ot->name = "Save As";
ot->idname = "WM_OT_save_as_mainfile";
ot->description = "Save the current file in the desired location";

View File

@ -561,7 +561,7 @@ static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link)
void WM_OT_link(wmOperatorType *ot)
{
ot->name = "Link from Library";
ot->name = "Link";
ot->idname = "WM_OT_link";
ot->description = "Link from a Library .blend file";
@ -585,7 +585,7 @@ void WM_OT_link(wmOperatorType *ot)
void WM_OT_append(wmOperatorType *ot)
{
ot->name = "Append from Library";
ot->name = "Append";
ot->idname = "WM_OT_append";
ot->description = "Append from a Library .blend file";

View File

@ -65,19 +65,27 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
0,
"Default",
"Automatically determine display type for files"},
{FILE_SHORTDISPLAY,
"LIST_SHORT",
ICON_SHORTDISPLAY,
{FILE_VERTICALDISPLAY,
"LIST_VERTICAL",
ICON_SHORTDISPLAY, /* Name of deprecated short list */
"Short List",
"Display files as short list"},
{FILE_LONGDISPLAY,
"LIST_LONG",
ICON_LONGDISPLAY,
{FILE_HORIZONTALDISPLAY,
"LIST_HORIZONTAL",
ICON_LONGDISPLAY, /* Name of deprecated long list */
"Long List",
"Display files as a detailed list"},
{FILE_IMGDISPLAY, "THUMBNAIL", ICON_IMGDISPLAY, "Thumbnails", "Display files as thumbnails"},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem file_action_types[] = {
{FILE_OPENFILE,
"OPENFILE",
0,
"Open",
"Use the file browser for opening files or a directory"},
{FILE_SAVE, "SAVE", 0, "Save", "Use the file browser for saving a file"},
};
if (flag & WM_FILESEL_FILEPATH) {
RNA_def_string_file_path(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Path to file");
@ -99,6 +107,15 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
if ((flag & WM_FILESEL_SHOW_PROPS) == 0) {
prop = RNA_def_boolean(ot->srna,
"hide_props_region",
true,
"Hide Operator Properties",
"Collapse the region displaying the operator settings");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
if (action == FILE_SAVE) {
/* note, this is only used to check if we should highlight the filename area red when the
* filepath is an existing file. */
@ -186,6 +203,9 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
prop = RNA_def_enum(
ot->srna, "sort_method", rna_enum_file_sort_items, sort, "File sorting mode", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_enum(ot->srna, "action_type", file_action_types, action, "Action Type", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
static void wm_operator_properties_select_action_ex(wmOperatorType *ot,

View File

@ -807,6 +807,7 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
ScrArea *sa;
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
eSpace_Type space_type = SPACE_EMPTY;
const char *title;
/* convert to native OS window coordinates */
@ -888,17 +889,24 @@ wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, i
CTX_wm_area_set(C, sa);
if (type == WM_WINDOW_RENDER) {
ED_area_newspace(C, sa, SPACE_IMAGE, false);
space_type = SPACE_IMAGE;
}
else if (type == WM_WINDOW_DRIVERS) {
ED_area_newspace(C, sa, SPACE_GRAPH, false);
space_type = SPACE_GRAPH;
}
else if (type == WM_WINDOW_USERPREFS) {
space_type = SPACE_USERPREF;
}
else if (type == WM_WINDOW_FILESEL) {
space_type = SPACE_FILE;
}
else if (type == WM_WINDOW_INFO) {
ED_area_newspace(C, sa, SPACE_INFO, false);
}
else {
ED_area_newspace(C, sa, SPACE_USERPREF, false);
BLI_assert(false);
}
ED_area_newspace(C, sa, space_type, false);
ED_screen_change(C, screen);
ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */