From de955c85fc44c2f437af523b4a19c2bf4a8e3cb7 Mon Sep 17 00:00:00 2001 From: Lester Caine Date: Thu, 4 Jun 2026 10:47:42 +0100 Subject: Icon system: switch from silk-sprite/colourstrap to Tango3 freedesktop iconset with SVG support - function.biticon.php: look in util/iconsets/ (moved from config/); add SVG fallback via scalable/ directory; biticon_first_match accepts optional extensions param; isize (small/medium/large) mapped to 16/24/32px for SVG width+height; isize added to ommit list so it never leaks as an HTML attribute - admin_themes_inc.php: iconset path updated to UTIL_PKG_PATH; biticon_sizes expanded to small (16px) / medium (24px) / large (32px) - admin_themes_manager.php: iconset path updated to UTIL_PKG_PATH - admin_themes_manager.tpl: replace stale Gnome/KDE links with Tango3/freedesktop refs - icon_browser.php: icon_fetcher now uses scalable/ SVGs as primary name source, supplements with large/ PNGs; all iconset paths updated to UTIL_PKG_PATH - icon_browser.tpl: three clean size columns (small/medium/large) using isize= instead of sloppy iname path embedding; help text updated Co-Authored-By: Claude Sonnet 4.6 --- admin/admin_themes_inc.php | 7 ++++--- admin/admin_themes_manager.php | 2 +- icon_browser.php | 22 +++++++++++++++++----- smartyplugins/function.biticon.php | 37 +++++++++++++++++++++++++------------ templates/admin_themes_manager.tpl | 2 +- templates/icon_browser.tpl | 38 ++++++++++++-------------------------- 6 files changed, 60 insertions(+), 48 deletions(-) diff --git a/admin/admin_themes_inc.php b/admin/admin_themes_inc.php index 54b0668..6200c88 100755 --- a/admin/admin_themes_inc.php +++ b/admin/admin_themes_inc.php @@ -76,15 +76,16 @@ $gBitSmarty->assign( "biticon_display_options", $biticon_display_options ); // get the icon styles $subDirs = [ 'style_info' ]; -$iconStyles = $gBitThemes->getStylesList( CONFIG_PKG_PATH."styles/icons/", false, $subDirs ); +$iconStyles = $gBitThemes->getStylesList( UTIL_PKG_PATH."iconsets/", false, $subDirs ); foreach( $iconStyles as $key=>$style ){ $iconStyles[$key] = str_replace( "_", " ", $style['style'] ); } $gBitSmarty->assign( "iconStyles", $iconStyles ); $biticon_sizes = [ - 'small' => KernelTools::tra( 'Small' ), - 'large' => KernelTools::tra( 'Large' ), + 'small' => KernelTools::tra( 'Small (16px)' ), + 'medium' => KernelTools::tra( 'Medium (24px)' ), + 'large' => KernelTools::tra( 'Large (32px)' ), ]; $gBitSmarty->assign( "biticon_sizes", $biticon_sizes ); diff --git a/admin/admin_themes_manager.php b/admin/admin_themes_manager.php index 8bac717..29101b2 100755 --- a/admin/admin_themes_manager.php +++ b/admin/admin_themes_manager.php @@ -42,7 +42,7 @@ $stylesList = $gBitThemes->getStylesList( '', false, $subDirs ); $gBitSmarty->assign( "stylesList", $stylesList ); $subDirs = [ 'style_info' ]; -$iconStyles = $gBitThemes->getStylesList( CONFIG_PKG_PATH."iconsets/", false, $subDirs ); +$iconStyles = $gBitThemes->getStylesList( UTIL_PKG_PATH."iconsets/", false, $subDirs ); $gBitSmarty->assign( "iconStyles", $iconStyles ); $styleLayouts = $gBitThemes->getStyleLayouts(); diff --git a/icon_browser.php b/icon_browser.php index 0cf4efe..9c6bda9 100755 --- a/icon_browser.php +++ b/icon_browser.php @@ -56,7 +56,7 @@ $gBitSmarty->assign( 'iconUsage', $iconUsage ); $iconList = []; $iconNames = []; -$iconThemes = ( !empty( $_REQUEST['icon_style'] ) ) ? [ $_REQUEST['icon_style'] ] : scandir( CONFIG_PKG_PATH . "iconsets/" ); +$iconThemes = ( !empty( $_REQUEST['icon_style'] ) ) ? [ $_REQUEST['icon_style'] ] : scandir( UTIL_PKG_PATH . "iconsets/" ); foreach( $iconThemes as $iconStyle ) { if( $icons = icon_fetcher( $iconStyle ) ) { @@ -74,12 +74,24 @@ $gBitSystem->display( 'bitpackage:themes/icon_browser.tpl', KernelTools::tra( 'I function icon_fetcher( $pStyle = DEFAULT_ICON_STYLE ) { $ret = []; if( strpos( $pStyle, '.' ) !== 0 && $pStyle != 'CVS' ) { - $stylePath = CONFIG_PKG_PATH."iconsets/".$pStyle; + $stylePath = UTIL_PKG_PATH."iconsets/".$pStyle; + + // Primary: scalable SVGs give the most complete name list + if( is_dir( $stylePath."/scalable" )) { + foreach( scandir( $stylePath."/scalable" ) as $icon ) { + if( preg_match( "#\.svg$#", $icon )) { + $name = substr( $icon, 0, -4 ); + $ret[$name] = $name; + } + } + } + + // Supplement with large PNGs not covered by scalable if( is_dir( $stylePath."/large" )) { - $handle = opendir( $stylePath."/large" ); - while( false !== ( $icon = readdir( $handle ))) { + foreach( scandir( $stylePath."/large" ) as $icon ) { if( preg_match( "#\.png$#", $icon ) && !preg_match( "#^process-working\.#", $icon )) { - $ret[str_replace( ".png", "", $icon )] = str_replace( ".png", "", $icon ); + $name = substr( $icon, 0, -4 ); + $ret[$name] ??= $name; } } } diff --git a/smartyplugins/function.biticon.php b/smartyplugins/function.biticon.php index 380b76d..a5ce139 100755 --- a/smartyplugins/function.biticon.php +++ b/smartyplugins/function.biticon.php @@ -18,13 +18,9 @@ use Bitweaver\KernelTools; * @access public * @return string|bool Icon name with extension on success, false on failure */ -function biticon_first_match( $pDir, $pFilename ) { +function biticon_first_match( $pDir, $pFilename, $pExtensions = ['png', 'gif', 'jpg'] ) { if( is_dir( $pDir )) { - global $gSniffer; - - $extensions = [ 'png', 'gif', 'jpg' ]; - - foreach( $extensions as $ext ) { + foreach( $pExtensions as $ext ) { if( is_file( $pDir.$pFilename.'.'.$ext ) ) { return $pFilename.'.'.$ext; } @@ -65,7 +61,7 @@ function biticon_output( $pParams, $pFile ) { $outstr .= ' alt=""'; } - $ommit = [ 'ilocation', 'ipackage', 'ipath', 'iname', 'iexplain', 'iforce', 'istyle', 'iclass' ]; + $ommit = [ 'ilocation', 'ipackage', 'ipath', 'iname', 'iexplain', 'iforce', 'istyle', 'iclass', 'isize' ]; foreach( $pParams as $name => $val ) { if( !in_array( $name, $ommit ) ) { $outstr .= ' '.$name.'="'.$val.'"'; @@ -82,6 +78,12 @@ function biticon_output( $pParams, $pFile ) { $outstr .= ' onclick="'.$pParams["onclick"].'"'; } + if( str_ends_with( $pFile, '.svg' ) ) { + $svgSizes = ['small' => 16, 'medium' => 24, 'large' => 32]; + $px = $svgSizes[$pParams['isize'] ?? 'small'] ?? 16; + $outstr .= " width=\"$px\" height=\"$px\""; + } + $outstr .= " />"; if( $gBitSystem->getConfig( 'site_biticon_display_style' ) == 'icon_text' && $pParams['iforce'] != 'icon' || $pParams['iforce'] == 'icon_text' ) { @@ -167,19 +169,30 @@ function smarty_function_biticon( $pParams, $pSmall = false ) { return $ret; } - // first deal with most common scenario: icon style ( a selected iconset from config/iconsets/ ) + // first deal with most common scenario: icon style ( a selected iconset from util/iconsets/ ) if( $pParams['ipackage'] == 'icons' ) { // get the current icon style // istyle is a private parameter!!! - only used on theme manager page for icon preview!!! // violators will be poked with soft cushions by the Cardinal himself!!! $icon_style = !empty( $pParams['istyle'] ) ? $pParams['istyle'] : $gBitSystem->getConfig( 'site_icon_style', DEFAULT_ICON_STYLE ); - if( false !== ( $matchFile = biticon_first_match( CONFIG_PKG_PATH."iconsets/$icon_style".$pParams['ipath'], $pParams['iname'] ))) { - return biticon_output( $pParams, CONFIG_PKG_URL."iconsets/$icon_style".$pParams['ipath'].$matchFile ); + if( false !== ( $matchFile = biticon_first_match( UTIL_PKG_PATH."iconsets/$icon_style".$pParams['ipath'], $pParams['iname'] ))) { + return biticon_output( $pParams, UTIL_PKG_URL."iconsets/$icon_style".$pParams['ipath'].$matchFile ); } - if( $icon_style != DEFAULT_ICON_STYLE && false !== ( $matchFile = biticon_first_match( CONFIG_PKG_PATH."iconsets/".DEFAULT_ICON_STYLE.$pParams['ipath'], $pParams['iname'] ))) { - return biticon_output( $pParams, CONFIG_PKG_URL."iconsets/".DEFAULT_ICON_STYLE.$pParams['ipath'].$matchFile ); + if( $icon_style != DEFAULT_ICON_STYLE && false !== ( $matchFile = biticon_first_match( UTIL_PKG_PATH."iconsets/".DEFAULT_ICON_STYLE.$pParams['ipath'], $pParams['iname'] ))) { + return biticon_output( $pParams, UTIL_PKG_URL."iconsets/".DEFAULT_ICON_STYLE.$pParams['ipath'].$matchFile ); + } + + // SVG fallback: raster not found, try scalable/ directory + $isize = trim( $pParams['ipath'], '/' ); + if( false !== ( $matchFile = biticon_first_match( UTIL_PKG_PATH."iconsets/$icon_style/scalable/", $pParams['iname'], ['svg'] ))) { + $pParams['isize'] = $isize; + return biticon_output( $pParams, UTIL_PKG_URL."iconsets/$icon_style/scalable/".$matchFile ); + } + if( $icon_style != DEFAULT_ICON_STYLE && false !== ( $matchFile = biticon_first_match( UTIL_PKG_PATH."iconsets/".DEFAULT_ICON_STYLE."/scalable/", $pParams['iname'], ['svg'] ))) { + $pParams['isize'] = $isize; + return biticon_output( $pParams, UTIL_PKG_URL."iconsets/".DEFAULT_ICON_STYLE."/scalable/".$matchFile ); } // if that didn't work, we'll try liberty diff --git a/templates/admin_themes_manager.tpl b/templates/admin_themes_manager.tpl index 75207e8..bde786e 100755 --- a/templates/admin_themes_manager.tpl +++ b/templates/admin_themes_manager.tpl @@ -49,7 +49,7 @@ {jstab title="Icon Theme"} {legend legend="Pick Icon Theme"}

- Icon themes can be downloaded from Gnome or KDE as long as they adhere to the Icon Naming Specifications. For more information, please visit IconThemes. + Icon sets must follow the freedesktop Icon Naming Specification. The active set is tango, sourced from Tango3 (stored in externals/Tango3). Place additional iconsets in util/iconsets/.

If you are a developer and you want to view a list of available icons, you can do this with the {smartlink ititle="Icon Browser" ifile="icon_browser.php"}. diff --git a/templates/icon_browser.tpl b/templates/icon_browser.tpl index 5c51cbc..43ed76f 100755 --- a/templates/icon_browser.tpl +++ b/templates/icon_browser.tpl @@ -6,18 +6,15 @@

- {tr}Very much work in progress, but getting there ... need to update the ross reference with current icon names rather than the desktop standard Tango ones!{/tr}
- Source of Fontawesome on GitHub for the monochrome icons.
- The font-awesome-to-png python script is used to create an image set for easier handling with the array display.
- Original source of the silk icon set for full colour icons.
- This is enhanced by the FatCow extended icon set which also provides 32x32 versions of the silk library
- See the Colourstrap information page for more data on replacing Bootstraps monochrome icons with traditional colour ones. + Icon sets use freedesktop icon naming. + The active set is tango, built from Tango3 with raster sizes at 16px (small), 24px (medium) and 32px (large), plus scalable SVGs. + SVGs are served automatically when no raster file exists for the requested size.

{foreach from=$iconList item=icons key=iconStyle} - {/foreach} + {/foreach} @@ -25,25 +22,14 @@ {foreach from=$iconNames item=name} {foreach from=$iconList item=icons key=iconStyle} - - - + {assign var=tdClass value=($gBitSystem->getConfig('site_icon_style') == $iconStyle) ? 'prio1' : (($iconStyle == $smarty.const.DEFAULT_ICON_STYLE) ? 'prio2' : '')} + {if $iconList.$iconStyle.$name} + + + + {else} + + {/if} {/foreach}
{$iconStyle}{tr}Icon name{/tr} {tr}bitweaver uses{/tr}
getConfig( 'site_icon_style' ) == $iconStyle}class="prio1"{elseif $iconStyle == $smarty.const.DEFAULT_ICON_STYLE}class="prio2"{/if}> - {if $iconList.$iconStyle.$name} - {* avoid translation here by not using iexplain *} - {biticon istyle=$iconStyle ipackage=icons iname="small/`$iconList.$iconStyle.$name`"} - {/if} - getConfig( 'site_icon_style' ) == $iconStyle}class="prio1"{elseif $iconStyle == $smarty.const.DEFAULT_ICON_STYLE}class="prio2"{/if}> - {if $iconList.$iconStyle.$name} - {* avoid translation here by not using iexplain *} - {biticon istyle=$iconStyle ipackage=icons iname="large/`$iconList.$iconStyle.$name`"} - {/if} - - {* only show huge size if looking at a particular set *} - {if $smarty.request.icon_style && $iconList.$iconStyle.$name} - {* avoid translation here by not using iexplain *} - {biticon istyle=$iconStyle ipackage=icons iname="huge/`$iconList.$iconStyle.$name`"} - {/if} - {biticon istyle=$iconStyle ipackage=icons iname=$name isize="small" iexplain=$name}{biticon istyle=$iconStyle ipackage=icons iname=$name isize="medium" iexplain=$name}{biticon istyle=$iconStyle ipackage=icons iname=$name isize="large" iexplain=$name} -- cgit v1.3